【问题标题】:how to pass stencil from a framebuffer to another如何将模板从帧缓冲区传递到另一个
【发布时间】:2015-11-14 03:33:42
【问题描述】:

我很难理解为什么在下面的代码中,当我直接渲染到屏幕时,模板测试按预期工作,而当我尝试使用帧缓冲区时却没有。我使用这些帧缓冲区对纹理进行多通道后处理没有问题,所以我怀疑我的深度和模板缓冲区有问题。

首先我初始化,上下文:

        gl = canvas.getContext("webgl",{stencil:true});

然后

function initFBO()
{

for (var i = 0; i < shaderPrograms.length; i++) 
{

    var texture = createTexture(imgWidth,imgHeight);
    textures.push(texture);

    var fbo = gl.createFramebuffer();
    framebuffers.push(fbo);

    gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);

    var renderbuffer = gl.createRenderbuffer();

    gl.bindRenderbuffer(gl.RENDERBUFFER, renderbuffer);
    gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_STENCIL, imgWidth, imgHeight);
    gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0);
    gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.RENDERBUFFER, renderbuffer);

}
}


function drawScene() 
{
gl.enable(gl.STENCIL_TEST);
gl.enable(gl.DEPTH_TEST);

if(!useFBO)
{
    computeProgFBO(9,null,imgHeight,imgHeight,textureId,null,null,false);
    computeProgFBO(8,null,imgHeight,imgHeight,textureId,null,null,false);
}else{
    computeProgFBO(9,framebuffers[0],imgHeight,imgHeight,textureId,null,null,false);
    computeProgFBO(8,null,imgHeight,imgHeight,textures[0],null,null,false);
}

// Restore the original matrix
mvPopMatrix();

gl.disable(gl.STENCIL_TEST);
gl.disable(gl.DEPTH_TEST);

}

function computeProgFBO(progId,fboId,w,h,textureId0,textureId1,textureId2,flip)
{

    currentProgram=shaderPrograms[progId];
    gl.useProgram(currentProgram);

    if(progId==9)
    {
        transform(w,h,0.0);
        gl.stencilMask(1);
        gl.stencilFunc(gl.ALWAYS,1,0);
        gl.stencilOp(gl.KEEP,gl.KEEP,gl.REPLACE);
        gl.colorMask(true, true, true, true);
        gl.depthMask(false);
        setMatrixUniformsView();
    }else{
        gl.colorMask(true, true, true, true);
        gl.depthMask(true);
        gl.stencilFunc(gl.EQUAL,0,1);
        gl.stencilOp(gl.KEEP,gl.KEEP,gl.KEEP);
        setMatrixUniforms();
    }

    setMyUniforms(progId,flip);
    setMyTexture(textureId0, textureId1,textureId2);

    gl.activeTexture(gl.TEXTURE1);
    gl.bindFramebuffer(gl.FRAMEBUFFER, fboId);
//the below is commented out for now as i'm only testing the stencil buffer for now        
//  gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, textures[progId], 0);




    gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0);

    gl.bindFramebuffer(gl.FRAMEBUFFER, null);
    gl.bindTexture(gl.TEXTURE_2D , null);
}

我认为展示不同的制服和纹理功能没有任何意义,但请务必让我知道。顺便说一句, progId 9 画了一个绿色的小方块,而 progId 8 在它上面画了一个更大的红色方块。所以,当我直接渲染到屏幕上时,我希望看到一个被一些红色边缘包围的绿色方块。

****在第一个答案的基础上,我已经修改了以下 *****

if(!useFBO)
{
    computeProgFBO(9,null,imgHeight,imgHeight,textureId,null,null,false,null);
    computeProgFBO(8,null,imgHeight,imgHeight,textureId,null,null,false,null);
    computeProgFBO(6,null,imgHeight,imgHeight,textureId,null,null,false,null);
}else{
    computeProgFBO(9,framebuffers[0],imgHeight,imgHeight,textureId,null,null,false,null);
    computeProgFBO(8,framebuffers[1],imgHeight,imgHeight,textures[0],null,null,false,renderbuffers[0]);
    computeProgFBO(6,null,imgHeight,imgHeight,textures[1],null,null,false,null);
}

我已将函数参数更改为

computeProgFBO(progId,fboId,w,h,textureId0,textureId1,textureId2,flip,render)

在下面的评论之后

//the below is commented out for now as i'm only testing the stencil buffer for now        
//  gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, textures[progId], 0);

我已经添加了

if(render!=null)
    {
        gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.RENDERBUFFER, render);
    }

结果稍微好一点,但还不是我所期望的,即:我得到了一个大的红色方块,而不是里面有一个小的绿色方块,我得到了一个白色的方块。我觉得这是一件微不足道的事情,但还不能真正解决。直接渲染到屏幕上仍然可以正常工作。

***** 对我添加的其他人的 cmets 进一步说明******

我想我理解了这个问题并找到了解决方法 [虽然不是很优雅]。基本上,第一遍使用模板写入帧缓冲区 [0] 的纹理 [0],第二遍使用帧缓冲区 [0] 的渲染缓冲区 [0] 写入帧缓冲区 [1] 的纹理 [1],但不写入纹理 [0] ]。因此,我最终得到了一个被红色包围的小白色 [而不是绿色] 正方形。因为,我不能 blit,我意识到我可以在第一遍中画两次,如下所示:

....
gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0);

if(progId==9) 
    {
        gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffers[1]);
        gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0);
    }

这可行,但看起来很丑!有没有其他办法?

在将最终纹理渲染到屏幕上时,我似乎不需要做太多事情,即:我只将颜色缓冲区(而不是模板和深度缓冲区)和所有最终纹理发送到屏幕按预期显示。

另外,请注意,我的片段着色器仅执行以下操作

shader9:gl_FragColor = vec4(0.0,1.0,0.0,1.0);

shader8:gl_FragColor = vec4(1.0,vec2(0.0),1.0);

着色器6:gl_FragColor = texture2D(u_Texture0,vTextureCoord);

【问题讨论】:

  • WebGL 是你最大的障碍...你有扩展 GL_ANGLE_framebuffer_blit 可用吗?
  • 你能澄清一下这里的障碍是什么吗?是因为我使用读写帧缓冲区并且由于某些原因从一个复制到下一个不起作用吗? (或者我应该说为纹理工作,而不是为深度和模板工作?)
  • 如果没有glBlitFramebuffer (...),复制模板缓冲区是不可能的。您不支持 WebGL 中的模板纹理,也没有复制渲染缓冲区的机制。
  • 您是否期望第一个渲染过程中的模板值将用于第二个过程?情况并非如此,因为您为 FBO 创建的模板缓冲区只会在您渲染到该 FBO 时使用,而不是在您渲染到默认帧缓冲区时使用。
  • @AndonM.Coleman :感谢您的评论,但根据 WacławJasper,似乎有一种方法可以在不使用 blit 的情况下做到这一点

标签: opengl webgl framebuffer stencil-buffer stencils


【解决方案1】:

您不能在 webgl 中复制缓冲区的内容,但您可以通过在同一个渲染缓冲区上调用 gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.RENDERBUFFER, renderbuffer); 在多个 FBOS 之间共享深度模板缓冲区。

如果要在启用模板测试的情况下渲染到默认帧缓冲区,则必须首先渲染到带有所需模板渲染缓冲区附件的 FBO。然后使用该 FBO 的颜色纹理绘制到默认帧缓冲区。

【讨论】:

  • 感谢您的提示。我已经按照建议修改了代码(参见问题的修正),但现在我没有一个被红色边框包围的绿色方块,我只有红色边框,绿色方块是白色的?
  • 我没有时间检查您的代码 atm。你做了我建议的第二部分吗?也就是说,您必须首先使用共享模板缓冲区渲染到另一个 FBO(在渲染到默认 FBO 之前),然后将该 FBO 的内容绘制到默认 FBO。
  • 您能否详细说明最后一步?我能够共享渲染缓冲区,但我无法将其用于渲染到主缓冲区。
猜你喜欢
  • 1970-01-01
  • 2015-01-22
  • 1970-01-01
  • 2019-09-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多