Getting Direct Access to the DepthBuffer in DirectX10
One of the cool things about DirectX 10 is that it allows you to get direct access to the depth buffer. This is in contrast to DirectX 9, which forces the hapless programmer to either run a separate rendering pass to get the depth information, which consumes more GPU cycles, or use Multiple Render Targets (MRTs) to get the depth information in a single pass at the cost of using additional memory. Needless to say, direct depth buffer access was a God send.
Or so I assumed. It was to my surprise that I couldn’t easily find DDBA (Direct Depth Buffer Access) information on the internets. It took a decent amount of searching (and playing around) to get the code up and working, but I’m glad to say that it works now!
Since it took me some time to get it working, I’ll go ahead and post what I did here to help anyone else attempting this handy technique.
Setting up the Depth View
The first thing you need to do to get DDBA working is setting up a view into your depth buffer that allows it to be bound to a shader. The code is a bit lengthy to post here directly, so I pasted it over at pastebin instead. The code is somewhat tailored for use with Claudius, but it should be raw enough to understand.
Accessing the Depth Buffer
Once you have the appropriate depth view set up, the next step is binding the depth buffer to the shader that needs it. But wait! The Depth Buffer cannot be set on the Graphics Device when you set it to the shader. If it is there will be no compile errors, but the depth buffer will be cleared to black. Getting around this problem is trivial, simply pass in NULL to the DepthStencilView portion of GraphicsDevice->OMSetRenderTargets. When you are finished with your shader, restore the buffer as before. I’m doing something like this:
GraphicsManager::GraphicsDevice->OMSetRenderTargets(1, &GraphicsManager::RenderTargetView, NULL);
//Set depth buffer to shader and render.
GraphicsManager::GraphicsDevice->OMSetRenderTargets(1, &GraphicsManager::RenderTargetView, GraphicsManager::DepthStencilView);
Working with the Shaders
When reading in the Depth Buffer inside your shader it is important that you do not use the Sample function. Sample applies Mip and SamplerState calculations to the result and thus could distort the data. Instead we want to read the data directly from the texture without modifying it in any way. This can be accomplished with the Load function. Load takes in a uint3 (A three unit vector of unsigned ints). The first two values are the x and y positions to sample, and the third value is the mip level to sample from (0 in our case). An interesting side note: Load does not use uv coordinates to sample from the texture. Rather, it uses actual pixel coordinates. We can get the correct positions by using the data inside the SV_POSITION semantic as shown in pastebin.
So there you go, you now have the basics necessary to get DDBA working! Oh, I should probably note one issue with this technique. DDBA does not work in DirectX10 if your depth buffer is multisampled. You will have to use 10.1 and up to get that feature. Our game currently does not use multisampling, so this isn’t an issue at the moment.
So now CJ can use the Depth Buffer information to build some more realistic water. I look forward to seeing what he comes up with :)