Я пытаюсь научиться делать несколько выходов из одной и той же программы в WebGL2, используя возможности gl.drawBuffer()
.
Я просмотрел книгу "Руководство по программированию OpenGL ES 3.0", глава 11, где перечислено, что необходимо для того, чтобы иметь место множественный вывод. Однако пример источника шейдера очень тривиален и выводит только постоянные значения.
Хотелось бы узнать, есть ли у кого-нибудь лучший пример? или можно ли объяснить, что случилось с изменением координат текстуры? В обычном коде шейдера я бы использовал это, чтобы найти значения данных из моих входных данных и записать их. Теперь, несмотря на наличие нескольких макетов, как меняющиеся координаты текстуры будут соответствовать каждому макету? Что происходит с размерами моего viewPort? какой выходной текстуре соответствует это?
Вот несколько шагов в моем понимании:
Для каждого FB:
вызвать drawBuffers, передавая массив прикрепления цветов
Внутри шейдера получаются следующие выходные значения:
layout(location = 0) out vec4 fragData0;
layout(location = 1) out vec4 fragData1;
Вам нужен только один объект фреймбуфера. Вы прикрепляете к нему все текстуры. Итак, ваши шаги будут
function main() {
const gl = document.querySelector('canvas').getContext('webgl2');
if (!gl) {
return alert("need WebGL2");
}
const vs = `
#version 300 es
void main() {
gl_PointSize = 300.0;
gl_Position = vec4(0, 0, 0, 1);
}
`;
const fs = `
#version 300 es
precision mediump float;
layout(location = 0) out vec4 outColor0;
layout(location = 1) out vec4 outColor1;
layout(location = 2) out vec4 outColor2;
layout(location = 3) out vec4 outColor3;
void main() {
outColor0 = vec4(1, .5, .3, .7); // orange
outColor1 = vec4(.6, .5, .4, .3); // brown
outColor2 = vec4(.2, .8, .0, 1); // green
outColor3 = vec4(.3, .4, .9, .6); // blue
}
`
const program = twgl.createProgram(gl, [vs, fs]);
const textures = [];
const fb = gl.createFramebuffer();
gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
for (let i = 0; i < 4; ++i) {
const tex = gl.createTexture();
textures.push(tex);
gl.bindTexture(gl.TEXTURE_2D, tex);
const width = 1;
const height = 1;
const level = 0;
gl.texImage2D(gl.TEXTURE_2D, level, gl.RGBA, width, height, 0,
gl.RGBA, gl.UNSIGNED_BYTE, null);
// attach texture to framebuffer
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0 + i,
gl.TEXTURE_2D, tex, level);
}
// our framebuffer textures are only 1x1 pixels
gl.viewport(0, 0, 1, 1);
// tell it we want to draw to all 4 attachments
gl.drawBuffers([
gl.COLOR_ATTACHMENT0,
gl.COLOR_ATTACHMENT1,
gl.COLOR_ATTACHMENT2,
gl.COLOR_ATTACHMENT3,
]);
// draw a single point
gl.useProgram(program);
{
const offset = 0;
const count = 1
gl.drawArrays(gl.POINT, 0, 1);
}
// --- below this is not relevant to the question but just so we
// --- we can see it's working
// render the 4 textures
const fs2 = `
#version 300 es
precision mediump float;
uniform sampler2D tex[4];
out vec4 outColor;
void main() {
vec4 colors[4];
// you can't index textures with non-constant integer expressions
// in WebGL2 (you can in WebGL1 lol)
colors[0] = texture(tex[0], vec2(0));
colors[1] = texture(tex[1], vec2(0));
colors[2] = texture(tex[2], vec2(0));
colors[3] = texture(tex[3], vec2(0));
vec4 color = vec4(0);
for (int i = 0; i < 4; ++i) {
float x = gl_PointCoord.x * 4.0;
float amount = step(float(i), x) * step(x, float(i + 1));
color = mix(color, colors[i], amount);
}
outColor = vec4(color.rgb, 1);
}
`;
const prgInfo2 = twgl.createProgramInfo(gl, [vs, fs2]);
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
gl.useProgram(prgInfo2.program);
// binds all the textures and set the uniforms
twgl.setUniforms(prgInfo2, {
tex: textures,
});
gl.drawArrays(gl.POINTS, 0, 1);
}
main();
<script src = "https://twgljs.org/dist/4.x/twgl.min.js"></script>
<canvas></canvas>
И, кроме того, как будет работать масштабирование? Я думаю, что в настоящее время я устанавливаю масштаб, устанавливая gl.viewport(0,0,width, height)
, и именно так я узнаю, что при получении vTexCoords, различающихся в моем фрагментном шейдере, мне пришлось бы умножить s и t на шкалы по X и Y, чтобы получить логические координаты. Итак, если разрешено выводить текстуры разного размера, мне также понадобятся различные коэффициенты масштабирования в качестве входных данных?
Текстуры, прикрепленные к одному и тому же фреймбуферу, не могут иметь разного размера. Все они должны быть одного размера. Я также не уверен, какое отношение texcoords имеет к области просмотра. В большинстве шейдеров эти 2 не связаны. Также вы создаете свои собственные атрибуты, так что вы не ограничены одним набором текстурных кордов, и вам даже не нужно иметь координаты текстур.
Еще раз спасибо за разъяснения.
Я пытаюсь сделать это, но мне нужно прикрепить еще одну текстуру после первого рисования. Я заметил, что вы привязываете фреймбуфер к NULL. Я не могу этого сделать. Однако, если я просто позвоню в gl.framebufferTexture2D()
, я получу статус FRAMEBUFFER_INCOMPLETE_DIMENSIONS
. Как мне сбросить мой FB после выполнения множественного вывода? Благодарность
Это старый, но я понятия не имею, что означает «сбросить мой fb». Вы должны сделать по одному fb для каждой комбинации вложений, которую хотите использовать. Вы не должны изменять fbs
Большое спасибо @gman. У меня все еще есть вопросы: все ли текстуры должны быть одного размера? Похоже, это хорошее решение для вывода разных цветов, но может быть не так полезно для вывода с произвольными размерами текстур. Если бы это было возможно, мне потребовались бы различные входные переменные координат текстуры для передачи в источник моего фрагмента.