Webgl-添加DEPTH_ STENCIL渲染缓冲区可防止渲染到立方体映射帧缓冲区



我们正在Webgl 1中工作,并试图使用模具渲染到立方体映射。对立方体贴图本身进行渲染效果良好。当我们添加DEPTH_STENCILrenderbuffer时,它会停止向立方体映射写入,并且不会发出错误。

  • 正常的TEXTURE_2D而不是TEXTURE_CUBE_MAP不会发生这种情况
  • 深度/模版/剪刀测试被禁用
  • framebufferRenderbuffer的调用破坏了它
  • 将渲染缓冲区切换为仅一个模具或仅一个深度具有相同的效果
  • 将渲染缓冲区切换为颜色缓冲区可以使其再次工作

这里有一个最小的娱乐活动。正如您所看到的,我们得到的控制台输出具有前三个调用的正确值和最后一个调用的零。

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

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个调用的相同值。您使用的是什么操作系统/GPU/驱动程序?如果你使用Chrome,你能粘贴你的about:gpu内容吗?

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

OpenGL ES规范不需要帧缓冲区附件的任何组合(零、zilch、nada(。WebGL规范需要3种组合才能工作。根据规范第6.8节:

当所有附件都是帧缓冲区附件完整、非零且宽度和高度相同时,帧缓冲区对象附件的以下组合必须导致帧缓冲区完成:

  • COLOR_ATTACHMENT0=RGBA/UNSIGNED_BYTE纹理
  • COLOR_ATTACHMENT0=RGBA/UNSIGNED_BYTE纹理+DEPTH_ATTACHMENT=DEPTH_COMPONENT16渲染缓冲区
  • COLOR_ATTACHMENT0=RGBA/UNSIGNED_BYTE纹理+DEPTH_STENCIL_ATTACHMENT=DEPTH_STANCIL渲染缓冲区

但是查看WebGL一致性测试,只测试TEXTURE_2D

因此,首先,这表明你的驱动程序/gpu不支持立方体映射的组合。通过调用gl.checkFramebufferStatus进行测试。如果它不返回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**");
}

你需要模具还是只需要深度缓冲?这个样品适合你吗?它使用DEPTH_COMPONENT16附件。