渲染到3D纹理webgl2

时间:2018-03-22 08:07:36

标签: javascript opengl-es glsl webgl

我读到here应该可以通过使用多个渲染目标并将3d纹理的每一层作为图层附加到渲染目标来渲染到WebGL2中的3D纹理。

但是我似乎无法让它工作,没有错误,但纹理的值在读取之间没有变化,只是空的。纹理的内部格式为gl.RGBA8,格式为gl.RGBA,大小为64x64x64

我做错了什么?这是我到目前为止所尝试的(伪代码):

this.fbo = gl.createFramebuffer();
gl.bindFramebuffer(gl.FRAMEBUFFER, this.fbo);
gl.TEXTURE_3D, this.my3DTexture, 0);

this.renderBuffer = gl.createRenderbuffer();
gl.bindRenderbuffer(gl.RENDERBUFFER, this.renderBuffer);
gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, 64, 64);    
gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, this.renderBuffer);

if (gl.checkFramebufferStatus(gl.FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE) {
  alert("FBO not complete!");
}

gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

this.shader.activate();

// Set uniforms ...

for (let i = 0; i < 64; i += 8) {
  gl.framebufferTextureLayer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, this.my3DTexture, 0, 0 + i);
  gl.framebufferTextureLayer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT1, this.my3DTexture, 0, 1 + i);
  gl.framebufferTextureLayer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT2, this.my3DTexture, 0, 2 + i);
  gl.framebufferTextureLayer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT3, this.my3DTexture, 0, 3 + i);
  gl.framebufferTextureLayer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT4, this.my3DTexture, 0, 4 + i);
  gl.framebufferTextureLayer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT5, this.my3DTexture, 0, 5 + i);
  gl.framebufferTextureLayer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT6, this.my3DTexture, 0, 6 + i);
  gl.framebufferTextureLayer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT7, this.my3DTexture, 0, 7 + i);

  gl.drawBuffers([
    gl.COLOR_ATTACHMENT0,
    gl.COLOR_ATTACHMENT1,
    gl.COLOR_ATTACHMENT2,
    gl.COLOR_ATTACHMENT3,
    gl.COLOR_ATTACHMENT4,
    gl.COLOR_ATTACHMENT5,
    gl.COLOR_ATTACHMENT6,
    gl.COLOR_ATTACHMENT7,
  ]);

  let data = new Uint8Array(64*64 * 4);
  gl.readPixels(0, 0, 64, 64, gl.RGBA, gl.UNSIGNED_BYTE, data);
  console.log("before", data);

  // Render scene
  scene.objects.forEach(object => {
    this._renderObject(object, scene, camera);
  });

  gl.readPixels(0, 0, 64, 64, gl.RGBA, gl.UNSIGNED_BYTE, data);
  console.log("after one iteration read", data);
}

片段着色器:

#version 300 es            
precision highp float;

layout(location = 0) out vec4 layer0;
layout(location = 1) out vec4 layer1;
layout(location = 2) out vec4 layer2;
layout(location = 3) out vec4 layer3;
layout(location = 4) out vec4 layer4;
layout(location = 5) out vec4 layer5;
layout(location = 6) out vec4 layer6;
layout(location = 7) out vec4 layer7;

void main() {
    layer0 = vec4(0.5, 0.5, 0.5, 1.0);
    layer1 = vec4(0.5, 0.5, 0.5, 1.0);
    layer2 = vec4(0.5, 0.5, 0.5, 1.0);
    layer3 = vec4(0.5, 0.5, 0.5, 1.0);
    layer4 = vec4(0.5, 0.5, 0.5, 1.0);
    layer5 = vec4(0.5, 0.5, 0.5, 1.0);
    layer6 = vec4(0.5, 0.5, 0.5, 1.0);
    layer7 = vec4(0.5, 0.5, 0.5, 1.0);
}

更新:它适用于gl.TEXTURE_2D_ARRAY,但失败并带有gl.TEXTURE_3D为什么?在测试here中,它们肯定会附加一个3d纹理层。

2 个答案:

答案 0 :(得分:1)

我正在尝试与您完全相同的尝试,但最终成功了,只是为将来的读者指出了一些警告。

TEXTURE_3D不兼容的2件事(从Chrome 84开始):

  1. 将深度缓冲区绑定到帧缓冲区将使事情搞砸,即使是单个纹理或纹理数组,我什至在浏览器中也遇到了麻烦。因此,如果您想使用TEXTURE_3D,请不要绑定任何深度缓冲区。
  2. 没有浮动的纹理,您没有使用过,但是它使我非常沮丧,以至于我想指出它。

如果有上述要求,您最好坚持使用gl.TEXTURE_2D_ARRAY

答案 1 :(得分:0)

示例中的AFAICT您只需要致电gl.drawArrays

您的样本正尝试渲染为8层。 WebGL2仅需要支持4层。您可以通过调用gl.getParameter(gl.MAX_DRAW_BUFFERS)来检查是否支持8,这可能是您的代码失败的另一个原因。

根据规范不是必需的,但无论如何可能都是使纹理纹理完整,这意味着使其可渲染(即使我们不使用它进行渲染,我们也正在对其进行渲染)。在这种情况下,因为我没有进行mips,所以我将TEXTURE_MIN_FILTER设置为LINEAR,因为默认情况下,它是NEAREST_MIPMAP_LINEAR,需要mips,否则纹理不可渲染。 不可渲染 = 不可使用期间至少在某些驱动程序中有效。

function main() {
  const gl = document.querySelector('canvas').getContext("webgl2");
  if (!gl) {
    return alert('need webgl2');
  }
  
  const vs = `#version 300 es
  void main() {
    gl_Position = vec4(0, 0, 0, 1);
    gl_PointSize = 10.0;
  }
  `;
  const fs = `#version 300 es
  precision mediump float;
  out vec4 outColor[4];
  void main() {
    outColor[0] = vec4(.1, .2, .3, .4);
    outColor[1] = vec4(.5, .6, .7, .8);
    outColor[2] = vec4(.9, 1., .11, .22);
    outColor[3] = vec4(.33, .44, .55, .66);
  }
  `;
  
  const program = twgl.createProgram(gl, [vs, fs]);
  
  const width = 4;
  const height = 4;
  const depth = 4;
  const tex = gl.createTexture(gl.TEXTURE_3D);
  gl.bindTexture(gl.TEXTURE_3D, tex);
  gl.texImage3D(gl.TEXTURE_3D, 0, gl.RGBA8, width, height, depth, 0,
                gl.RGBA, gl.UNSIGNED_BYTE, null);
  gl.texParameteri(gl.TEXTURE_3D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
  
  const numAttachments = 4;
  const fb = gl.createFramebuffer();
  gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
  for (let i = 0; i < numAttachments; ++i) {
    gl.framebufferTextureLayer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0 + i, tex, 0, i);
  }
  
  gl.drawBuffers([
    gl.COLOR_ATTACHMENT0, 
    gl.COLOR_ATTACHMENT1, 
    gl.COLOR_ATTACHMENT2, 
    gl.COLOR_ATTACHMENT3,
  ]);
  
  gl.viewport(0, 0, width, height);
  gl.useProgram(program);
  gl.drawArrays(gl.POINTS, 0, 1);
  
  for (let i = 0; i < numAttachments; ++i) {
    gl.readBuffer(gl.COLOR_ATTACHMENT0 + i);
    const pixel = new Uint8Array(4);
    gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, pixel);
    console.log('layer:', i, '=', Array.from(pixel).map(v => (v / 255).toFixed(2)).join(', '));
  }
}
main();
    
<script src="https://twgljs.org/dist/4.x/twgl.min.js"></script>
<canvas></canvas>