Restoring the Depth Buffer

A long time ago, when I wrote the Deferred Rendering article, in the last chapter I mentioned adding transparent objects to the rendering pipeline. To do that, we would need to restore the depth buffer, as if the objects in the scene were there. This information was lost when we began applying the different passes of deferred rendering.

In this month’s sample, I’ll show you how to restore the depth buffer, using the information stored inside  a depth texture.

To make an example out of this, the sample does the following things:

  1. Render a ship on the scene, once on a render target that holds the color, and once on a render target that holds depth information
  2. Apply post processing effects on the color texture. In this sample, I simply blur the image a little, and add sepia
  3. Restore the color information into the back buffer, and restore depth values into the depth buffer
  4. Draw another ship

If at step 3, we would only restore the color information, the resulting image would have incorrect object intersections. When doing post processing on the initial scene, we lose all depth information, so the ship drawn at step 4 would appear in front of everything else

DepthRecovery 2008-05-19 02-30-52-57

By restoring the depth buffer also, we can see how parts of the post processed image that should appear in front of the ship drawn at step 4 actually appear in front of it.

DepthRecovery 2008-05-19 02-30-53-54

To achieve this, we used the DEPTH pixel shader output semantic. This semantic is used in a pixel shader to write a specific value to the depth buffer. By taking this values from the depth texture generated at step 1, you can actually restore the contents of the depth buffer. The shader code that does this can be found in RestoreBuffers.fx.

void RestoreBuffersPixelShader(in float2 texCoord : TEXCOORD0,
                                 out float4 color: COLOR0,
                                 out float depth : DEPTH)
{
    //write the color
    color = tex2D(ColorSampler, texCoord);

    //write the depth
    depth = tex2D(DepthSampler, texCoord).r;
}

When running the sample, use the R keyboard key to toggle the restoration of the depth buffer, and the P key to toggle post processing.

The sample is written in 3.0 CTP, but the technique works just as well in 2.0

The sample can be downloaded here: DepthRecovery.zip

  • Pingback: Catalin Zima - XNA and HLSL blog » Can a 2D image intersect a 3D model?

  • http://www.pjstyle.net Ted de Vries

    Hey,

    I see you have a full blown sample about the question i asked last week. Nice! 🙂

    I solved my problem by the way!

    Regards,

    Ted

  • http://videogameprogramming.blogspot.com/ Brian L

    Nice example! One quick note. When sampling from a texture containing items such as depth, etc., you’ll want to use POINT sampling and not LINEAR.

  • http://www.catalinzima.com Catalin Zima

    You’re right. But when the texture is the exact same size as the destination, it makes little to no difference, as long as the texels are properly aligned.

    But indeed, POINT would probably be better.

  • Anton

    Hi Catalin,

    Let me start off by saying that you saved me! I am currently implementing your Deferred Renderer together with XNA’s XML Particles sample and this tutorial helped me a lot to place the particles correctly in the scene instead of on top of the scene.

    I just have a question though. Upon using the RestoreBuffers method in your sample and supplying the combined scene render target instead of “sceneRT”, I am getting some black border artifacts. Do I still need to do some texel offsetting in the RestoreBuffer.fx shader?

  • http://www.catalinzima.com Catalin Zima

    You shouldn’t need to, since SpriteBatch takes care of aligning pixels to texels.

    Do the black borders appear all around, or only on some edges (left and top) ?

  • Anton

    Only the left and top. I tried to modify the offsets in the Combine Final shader. Instead of subtracting the offset, I added the offset. Adding 3 times the halfpixel offset seemed to remove the border problem but now the render seems to be a bit blurry, probably 1 to 2 pixels of blur.

  • http://www.catalinzima.com Catalin Zima

    Try and use POINT sampling when reading from the depth/color textures, and see if it solves anything.

  • Anton

    Ok thanks. Thank you for the quick reply. I’m still at the office, so it’ll be sometime until I can try it out at home. If things don’t go well, I’ll start a thread in the Creator Club forums and place in full details there. Thanks again.

  • http://Website Linchy

    Thanks very much for the sample, just what I was looking for.

    For anyone interested, I converted the project to XNA4 and uploaded it here: http://www.2shared.com/file/7hQ8ux90/RestoreDepthBufferXNA4.html?

  • http://speps.fr Remi Gillig

    @Linchy : Line 69 in Game1.cs should be

    depthRT = new RenderTarget2D(GraphicsDevice, pp.BackBufferWidth, pp.BackBufferHeight, false, SurfaceFormat.Single, DepthFormat.Depth24);

    The modification is assigning a depth buffer instead of none, it’s still useful during the depth writing pass.