Webgl - 添加 DEPTH_STENCIL 渲染缓冲区阻止渲染到立方体贴图帧缓冲区

Webgl - adding DEPTH_STENCIL renderbuffer prevents rendering to cubemap framebuffer

我们正在使用 Webgl 1 并尝试使用模板渲染到立方体贴图。单独渲染到立方体贴图效果很好。当我们添加 DEPTH_STENCIL 渲染缓冲区时,它会停止写入立方体贴图并且不会发出任何错误。

这是一个简单的娱乐活动。如您所见,我们得到了一个控制台输出,其中前三个调用的值正确,最后一个调用的值为零。

为什么会发生这种情况?我们缺少什么让渲染缓冲区与立方体贴图一起工作?

const canvas = document.createElement("canvas");
const gl = canvas.getContext("webgl");

console.log(TEST(false, false));
console.log(TEST(false, true));
console.log(TEST(true, false));
console.log(TEST(true, true));

function TEST(useCubemap, useBuffer) {
  const size = 512;
  const textureType = useCubemap ? gl.TEXTURE_CUBE_MAP : gl.TEXTURE_2D;

  // SETUP THE PROGRAM
  {
    const program = gl.createProgram();
    const vertShader = gl.createShader(gl.VERTEX_SHADER);
    const fragShader = gl.createShader(gl.FRAGMENT_SHADER);

    gl.shaderSource(vertShader, `
            attribute vec2 a_position;
            
            void main() {           
                gl_Position = vec4(a_position, 0.2, 1.0);
            }
        `);
    gl.compileShader(vertShader);
    gl.attachShader(program, vertShader);

    gl.shaderSource(fragShader, `
            void main() {
                gl_FragColor = vec4(0.1, 0.2, 0.3, 0.4);
            }
        `);
    gl.compileShader(fragShader);
    gl.attachShader(program, fragShader);

    gl.linkProgram(program);
    gl.useProgram(program);
  }

  // SETUP THE QUAD
  {
    const posBuffer = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, posBuffer);
    gl.enableVertexAttribArray(0);
    gl.vertexAttribPointer(0, 2, gl.FLOAT, false, 0, 0);
    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([-1, +1, -1, -1, +1, +1, +1, -1]), gl.STATIC_DRAW);
  }

  // SETUP THE FRAMEBUFFER
  {
    const fb = gl.createFramebuffer();
    const targetTexture = gl.createTexture();

    gl.bindFramebuffer(gl.FRAMEBUFFER, fb);

    gl.bindTexture(textureType, targetTexture);
    gl.texParameteri(textureType, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
    gl.texParameteri(textureType, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
    gl.texParameteri(textureType, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
    gl.texParameteri(textureType, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);

    // SWITCH TEXTURE TYPE
    if (textureType === gl.TEXTURE_2D) {
      gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, size, size, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
      gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, targetTexture, 0);
    } else {
      for (let i = 0; i < 6; i++) gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, gl.RGBA, size, size, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
      gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_CUBE_MAP_POSITIVE_X, targetTexture, 0);
    }
  }

  // SETUP THE RENDER BUFFER
  {
    const rb = gl.createRenderbuffer();

    gl.bindRenderbuffer(gl.RENDERBUFFER, rb);
    gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_STENCIL, size, size);

    // TAKING THIS OUT MAKES IT WORK
    if (useBuffer) gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.RENDERBUFFER, rb);
  }


  // DISABLE THE OBVIOUS CULPRITS
  gl.disable(gl.DEPTH_TEST);
  gl.disable(gl.STENCIL_TEST);
  gl.disable(gl.SCISSOR_TEST);

  // DO A RENDERYFUCK
  gl.viewport(0, 0, size, size);
  gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);

  // GET THE OUTFUCK
  const pixels = new Uint8Array(4);
  gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, pixels);
  return pixels;
}

对我有用。我在您发布的代码中获得了所有 4 个调用的相同值。 OS/GPU/Driver 你在用什么?如果您使用的是 Chrome,可以粘贴您的 about:gpu 内容吗?

这听起来像是您的驱动程序中的错误。这也可能是 WebGL 规范中的一个错误。

OpenGL ES 规范 需要帧缓冲区附件的任何组合才能工作(零、zilch、nada)。 WebGL 规范需要 3 种组合才能工作。来自 the spec, section 6.8:

The following combinations of framebuffer object attachments, when all of the attachments are framebuffer attachment complete, non-zero, and have the same width and height, must result in the framebuffer being framebuffer complete:

  • COLOR_ATTACHMENT0 = RGBA/UNSIGNED_BYTE texture
  • COLOR_ATTACHMENT0 = RGBA/UNSIGNED_BYTE texture + DEPTH_ATTACHMENT = DEPTH_COMPONENT16 renderbuffer
  • COLOR_ATTACHMENT0 = RGBA/UNSIGNED_BYTE texture + DEPTH_STENCIL_ATTACHMENT = DEPTH_STENCIL renderbuffer

但是查看 WebGL 一致性测试 only TEXTURE_2D is tested

所以,首先表明您的 driver/gpu 不支持与立方体贴图的组合。通过调用 gl.checkFramebufferStatus 进行测试。如果它不支持 return gl.FRAMEBUFFER_COMPLETE 您的设置不支持渲染到带有深度模板附件的立方体贴图。

const canvas = document.createElement("canvas");
const gl = canvas.getContext("webgl");

TEST("TEXTURE_2D", "DEPTH_COMPONENT16");
TEST("TEXTURE_2D", "DEPTH_STENCIL");
TEST("TEXTURE_CUBE_MAP", "DEPTH_COMPONENT16");
TEST("TEXTURE_CUBE_MAP", "DEPTH_STENCIL");

function TEST(target, depthBufferFormat) {
  const size = 16;
  const textureType = gl[target];

  // SETUP THE FRAMEBUFFER
  {
    const fb = gl.createFramebuffer();
    const targetTexture = gl.createTexture();

    gl.bindFramebuffer(gl.FRAMEBUFFER, fb);

    gl.bindTexture(textureType, targetTexture);
    gl.texParameteri(textureType, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
    gl.texParameteri(textureType, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
    gl.texParameteri(textureType, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
    gl.texParameteri(textureType, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);

    // SWITCH TEXTURE TYPE
    if (textureType === gl.TEXTURE_2D) {
      gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, size, size, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
      gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, targetTexture, 0);
    } else {
      for (let i = 0; i < 6; i++) gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, gl.RGBA, size, size, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
      gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_CUBE_MAP_POSITIVE_X, targetTexture, 0);
    }
  }

  // SETUP THE RENDER BUFFER
  {
    const rb = gl.createRenderbuffer();
    const format = gl[depthBufferFormat];

    gl.bindRenderbuffer(gl.RENDERBUFFER, rb);
    gl.renderbufferStorage(gl.RENDERBUFFER, format, size, size);

    // TAKING THIS OUT MAKES IT WORK
    const attachmentPoint = depthBufferFormat === "DEPTH_COMPONENT16"
      ? gl.DEPTH_ATTACHMENT
      : gl.DEPTH_STENCIL_ATTACHMENT;
    gl.framebufferRenderbuffer(gl.FRAMEBUFFER, attachmentPoint, gl.RENDERBUFFER, rb);
  }

  const success =  gl.checkFramebufferStatus(gl.FRAMEBUFFER) === gl.FRAMEBUFFER_COMPLETE;
  console.log(target, depthBufferFormat, success ? "PASS" : "**FAIL**");
}

您需要模板还是只需要深度缓冲区? this sample run 适合你吗?它正在使用 DEPTH_COMPONENT16 附件。