2

我正在尝试学习如何在 WebGL2 中利用gl.drawBuffer()功能从同一个程序中进行多个输出。

我查看了《OpenGL ES 3.0 Programming Guide》一书的第 11 章,其中列出了发生多输出所需的内容。然而,着色器源示例非常简单,仅输出常量值。

我想知道是否有人有更好的例子?或者是否可以解释 TextureCoordinates 变化发生了什么?在普通的着色器代码中,我会使用它从我的输入中查找数据值并将它们写出来。现在面对多种布局,TextureCoordinates怎么变化对应每个布局呢?我的视口尺寸会发生什么变化?这与哪个输出纹理对应?

以下是我理解它们的一些步骤:

  1. 创建一个颜色附件数组 GL_COLOR_ATTACHMENT0, ...
  2. 为每个输出创建一个帧缓冲区对象
  3. 创建输出纹理
  4. 对于每个FB:

    • 绑定帧缓冲区
    • 绑定纹理
    • 将纹理与 FBO 关联:frameBufferTexture2D (..., color_attchment_from_step1)
  5. 调用 drawBuffers 传递颜色附件数组

在着色器内部访问输出值,如下所示:

layout(location = 0) out vec4 fragData0;

layout(location = 1) out vec4 fragData1;
4

1 回答 1

7

您只需要一个帧缓冲区对象。您将所有纹理附加到它。所以你的步骤是

  1. 创建一个帧缓冲区对象和 BindFramebuffer
  2. 创建输出纹理
  3. 对于每个纹理:
  4. 将纹理与 FBO 关联:frameBufferTexture2D(...)
  5. 创建一个颜色附件数组 GL_COLOR_ATTACHMENT0, ...
  6. 调用 drawBuffers 传递颜色附件数组

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>

于 2018-08-11T08:55:06.763 回答