javascriptwebglframebufferdepth-bufferwebgl-extensions

WebGL FrameBuffer - Render Depth Texture


I am in the process of integrating post processing effects into my 3D engine. I have hit a roadblock with capturing depth data using a FrameBuffer Object with WebGL. There has been no issue capturing color data into the FrameBuffer. However, I am not able to get any Depth data even with the Depth Extension enabled for Chrome/Firefox.

var DepthEXT = webGLContext.getExtension( "WEBKIT_WEBGL_depth_texture" ) ||
                webGLContext.getExtension( "MOZ_WEBGL_depth_texture" );

I have tried many different settings to see if it was a configuration issue, but no matter what I try I just see a white texture. The screenshot at the end of the post shows the color attachment rendered as a texture and the depth attachment rendered as a texture. Is there a problem with my initialization of the FrameBuffer or should I be looking elsewhere to solve this issue?

Below is my code for initializing the FrameBuffer Object:

    // Frame Buffer
    this.m_frameBuffer = gl.createFramebuffer();
    gl.bindFramebuffer( gl.FRAMEBUFFER, this.m_frameBuffer );

    // Render Buffer
    this.m_renderBuffer = gl.createRenderbuffer();
    gl.bindRenderbuffer( gl.RENDERBUFFER, this.m_renderBuffer );
    gl.renderbufferStorage( 
        gl.RENDERBUFFER,
        gl.DEPTH_COMPONENT16,
        sharedRenderer.canvasDOMElement.width,
        sharedRenderer.canvasDOMElement.height );

    // Diffuse Component
    this.m_diffuseComponentTexture = gl.createTexture();
    gl.bindTexture( gl.TEXTURE_2D, this.m_diffuseComponentTexture );

    gl.texParameteri( 
        gl.TEXTURE_2D,
        gl.TEXTURE_MAG_FILTER,
        gl.LINEAR );

    gl.texParameteri( 
        gl.TEXTURE_2D, 
        gl.TEXTURE_MIN_FILTER, 
        gl.LINEAR );

    gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
    gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);

    gl.texImage2D( 
        gl.TEXTURE_2D,
        0,
        gl.RGBA,
        sharedRenderer.canvasDOMElement.width,
        sharedRenderer.canvasDOMElement.height,
        0,
        gl.RGBA,
        gl.UNSIGNED_BYTE, 
        null );

    // Depth
    this.m_depthComponentTexture = gl.createTexture();
    gl.bindTexture( gl.TEXTURE_2D, this.m_depthComponentTexture );

    gl.texParameteri( 
        gl.TEXTURE_2D,
        gl.TEXTURE_MAG_FILTER,
        gl.NEAREST );

    gl.texParameteri( 
        gl.TEXTURE_2D, 
        gl.TEXTURE_MIN_FILTER, 
        gl.NEAREST );

    gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
    gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);

    gl.texImage2D( 
        gl.TEXTURE_2D,
        0,
        gl.DEPTH_COMPONENT,
        sharedRenderer.canvasDOMElement.width,
        sharedRenderer.canvasDOMElement.height,
        0,
        gl.DEPTH_COMPONENT,
        gl.UNSIGNED_SHORT, 
        null );

    // FrameBuffer
    // Diffuse
    gl.framebufferTexture2D( 
        gl.FRAMEBUFFER,
        gl.COLOR_ATTACHMENT0, 
        gl.TEXTURE_2D, 
        this.m_diffuseComponentTexture, 
        0 );

    // Depth
    gl.framebufferTexture2D( 
        gl.FRAMEBUFFER,
        gl.DEPTH_ATTACHMENT, 
        gl.TEXTURE_2D, 
        this.m_depthComponentTexture, 
        0 );

    // RenderBuffer
    gl.framebufferRenderbuffer( 
        gl.FRAMEBUFFER,
        gl.DEPTH_ATTACHMENT, 
        gl.RENDERBUFFER, 
        this.m_renderBuffer );


    // Unbind buffers and textures
    gl.bindTexture( gl.TEXTURE_2D, null );
    gl.bindRenderbuffer( gl.RENDERBUFFER, null );
    gl.bindFramebuffer( gl.FRAMEBUFFER, null );

Here is the code where I render the current scene to the FrameBuffer.

CBRenderer.prototype.renderSceneToGBuffer = function( sceneToRender, GBufferTarget, deltaSeconds )
{
        CBMatrixStack.clearMatrixStackAndPushIdentityMatrix();

        this.applyProjectionMatrix();

        GBufferTarget.bindGBufferFrameBuffer();

        this.renderer.enable( this.renderer.DEPTH_TEST );
        this.renderer.depthMask( true );
        this.renderer.clearDepth( 1.0 );
        this.renderer.clearColor( 0.1, 0.1, 0.1, 0.0 );

        this.renderer.clear( this.renderer.COLOR_BUFFER_BIT | this.renderer.DEPTH_BUFFER_BIT );

        sceneToRender.render( deltaSeconds );

        this.renderer.flush();

        GBufferTarget.m_dirty = false;
        GBufferTarget.unbindGBufferFrameBuffer();

        this.renderer.clearColor( 0.0, 0.0, 0.0, 0.0 );
        this.renderer.clear( this.renderer.COLOR_BUFFER_BIT | this.renderer.DEPTH_BUFFER_BIT );

        this.renderer.bindTexture( this.renderer.TEXTURE_2D, null );
}

enter image description here


Solution

  • You don't have the depth texture attached to your FBO at the end of setting it up. You do set the texture as the depth attachment at some point:

    // Depth
    gl.framebufferTexture2D( 
        gl.FRAMEBUFFER,
        gl.DEPTH_ATTACHMENT, 
        gl.TEXTURE_2D, 
        this.m_depthComponentTexture, 
        0 );
    

    But immediately following that, you set a renderbuffer as the depth attachment instead:

    // RenderBuffer
    gl.framebufferRenderbuffer( 
        gl.FRAMEBUFFER,
        gl.DEPTH_ATTACHMENT, 
        gl.RENDERBUFFER, 
        this.m_renderBuffer );
    

    You can have only one target attached to each FBO attachment point. So the second call sets a new depth attachment, replacing the one you just set in the first call. So at the end of this, m_depthComponentTexture is not attached to the FBO anymore.

    If you want to use a depth texture, I don't see why you would want to create a depth renderbuffer as well. You should only need one of the two. You would normally use a renderbuffer for the depth if you don't need it for sampling later. If you want to sample from the result, you need a texture instead, and there's no need for a renderbuffer anymore.