3

I'm struggling to understand why in the code below, the stencil tests work as expected when i render straight to the screen and doesnt when i attempt to use framebuffers. I have no problem using these framebuffers for multipass postprocessing on textures, and so i suspect i'm doing something wrong with my depth & stencil buffers.

First i initialise, the context:

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

then

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);
}

I dont think there is any point in showing the different uniforms and texture functions, but please do let me know otherwise. Btw, the progId 9 draws a small green square, and progId 8 a bigger red square on top of it. So, i'm expecting to see a green square surrounded by some red edges as it currently show when i render straight to the screen.

****FURTHER to the 1st answer i have amended the below *****

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);
}

and i have changed the function arguments to

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

and in there after the below comment

//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);

i have added

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

The result is slightly better but not yet exactly what i was expecting, ie: im getting the big red square and instead of having the little green square inside, i get a white square. I feel like this is something trivial but can't really work it out just yet. Rendering straight to the screen still works fine.

***** FURTHER to comments from others i added******

i think i understand the problem and found a workaround for it [not very elegant tho]. Basically, the first pass writes using stencil to textures[0] of framebuffers[0], and the 2nd pass writes to textures[1] of framebuffers[1] using renderbuffers[0] of framebuffers[0], but not textures[0]. Hence i end up with just a small white[instead of green] square surrounded by a red one. Since, i cant blit, i've realised that i can just draw twice in the first pass as per the below:

....
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);
    }

This works but looks very ugly! Is there any other way?

When it comes to render the final texture to the screen, it seems like i dont have to do much, ie: i only send the color buffer to the screen (and not the stencil & depth buffer) and all the final texture gets displayed as expected.

Also, note that my fragment shaders are only doing the below

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

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

shader6: gl_FragColor = texture2D(u_Texture0,vTextureCoord);

  • WebGL's your biggest obstacle... do you have the extension [`GL_ANGLE_framebuffer_blit`](https://www.khronos.org/registry/gles/extensions/ANGLE/ANGLE_framebuffer_blit.txt) available? – Andon M. Coleman Aug 20 '15 at 13:50
  • could you clarify what is the obstacle here? is it because im using read&write framebuffers and for some reasons the copy from one to the next doesnt work? (or should i say work for texture but not for depth and stencil?) – RogerLePatissier Aug 20 '15 at 14:10
  • 1
    Copying the stencil buffer is impossible without `glBlitFramebuffer (...)`. You do not have support for stencil textures in WebGL and no mechanism to copy renderbuffers. – Andon M. Coleman Aug 20 '15 at 14:42
  • Are you expecting that the stencil values from the first rendering pass will be used for the second pass? This can't be the case because the stencil buffer you created for the FBO will only be used when you render to that FBO, and not when you render to the default framebuffer. – Reto Koradi Aug 20 '15 at 14:51
  • @AndonM.Coleman : thanks for your comment but as per WacławJasper, it seems that there is a way to do this without using blit – RogerLePatissier Aug 21 '15 at 10:21
  • @RetoKoradi : thanks for your comment which i guess as inspired WacławJasper in his answer, and so it seems that his suggested workaround should be good enough – RogerLePatissier Aug 21 '15 at 10:21
  • I still think your problem here is `computeProgFBO(6,null,imgHeight,imgHeight,textures[1],null,null,false,null);`. That pass will never work the way you want because you cannot attach a stencil buffer RBO to the default framebuffer. At the very least, you need to disable the stencil test before/during that render pass. – Andon M. Coleman Aug 21 '15 at 15:03

1 Answers1

2

You can not copy the contents of buffers in webgl, but you can share the depth-stencil buffer between many FBOS by calling gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.RENDERBUFFER, renderbuffer); on the same renderbuffer.

If you want to render to the default framebuffer with stencil test enabled, you must first render to a FBO with the desired stencil renderbuffer attachment. Then just draw to the default framebuffer using that FBO's color texture.

WacławJasper
  • 3,284
  • 14
  • 19
  • thank you for the tip. I have amended the code as advised (cf amendment on the question), but now instead of having a green square surrounded by a red border, i only have the red border and the green square is white? – RogerLePatissier Aug 21 '15 at 10:08
  • I dont have the time to go over your code atm. Did you do the second part I suggested? That is you must first render to another FBO (before rendering to default FBO) with the shared stencil buffer first and then draw the content of that FBO to the default FBO. – WacławJasper Aug 23 '15 at 04:36
  • Can you elaborate a bit on the last step? I was able to share the render buffer but i'm unable to use it for rendering into the main buffer. – pailhead Dec 19 '18 at 21:47