WebGL FrameBuffer - Render Depth Texture

5.1k views Asked by At

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

1

There are 1 answers

0
Reto Koradi On BEST ANSWER

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.