If you have to deal with Depth Buffer reading in Direct3D9, you’ll propably encounter the documentation saying that you cannot read the GPU depth buffer directly. But there’s a workaround with special buffer formats on NVIDIA Cards (RAWZ on older cards and INTZ on G80+ series) and on ATI (DF16/24, I think). There’s also a paragraph in the G80 documentation by NVIDIA (see http://developer.nvidia.com/object/gpu_programming_guide.html). I think GTA4 is a game that uses the formats. Here’s some code I have been playing around with using Microsofts DirectX SDK of November 2008:

// decls

IDirect3DTexture9* m_pSMZTexture;   // rawz for using dss as input for next shader
LPDIRECT3DSURFACE9 m_pBackBuffer;   // default

// creates NVIDIA INTZ Format DSS. Use as DSS and render scene. Then use as input texture for another pass.
BOOL vmD3D::SetupDepthBufferAccess()
{
if(!m_pd3dDevice)
return FALSE;

if(FAILED(m_pd3dDevice->CreateTexture(TEXDEPTH_WIDTH, TEXDEPTH_HEIGHT, 1,
D3DUSAGE_DEPTHSTENCIL, (D3DFORMAT)MAKEFOURCC('I','N','T','Z'),
D3DPOOL_DEFAULT, &m_pSMZTexture, NULL)))
{
return FALSE;
}

return TRUE;
}

This actually creates an INTZ-Format DSS. Be sure to use the same dimensions of the render target you’ll bind on the same drawcall. Next, before your dracall bind the dss:

// setdss (our custom zsurf) -> intz!
// first fetch surface from tex at level 0
IDirect3DSurface9 *pSMZSurf = NULL;
m_pSMZTexture->GetSurfaceLevel(0, &pSMZSurf);
// set dss:
if(FAILED(m_pd3dDevice->SetDepthStencilSurface(pSMZSurf)))
return FALSE;
pSMZSurf->Release();

When drawing, there’s no need for any shader stuff if you simply want to output depth-only values (just write black color for example). But obviosly your vertex shader should do the needed transformations (multiply your WorldProjectionMatrix for example). After drawing with the intz-dss bound, you can use the texture as shader input for a next render pass. Here you can simply render a Full-Screen Quad to visualize the depth values:


if(FAILED(pFxDisplayDepth->SetTexture("DepthMap", m_pSMZTexture)))
return FALSE;

//set render target back to normal back buffer / depth buffer (or disable Z - as it's a dumb quad!)
if(FAILED(m_pd3dDevice->SetRenderTarget(0, m_pBackBuffer)))
return FALSE;

Then just draw the quad, i.e. with lazy UP RHW drawing:


UINT uPasses;
if (SUCCEEDED(pFxDisplayDepth->Begin(&uPasses, 0)))
{
for (UINT uPass = 0; uPass < uPasses; uPass++)
{
pFxDisplayDepth->BeginPass(uPass);
m_pd3dDevice->SetFVF( m_pQuad->FVF );
m_pd3dDevice->DrawPrimitiveUP( D3DPT_TRIANGLESTRIP, 2, m_pQuad->m_Vertex, sizeof( FullScreenQuad::Vertex ) );
pFxDisplayDepth->EndPass();
}
pFxDisplayDepth->End();
}

Be aware that Microsoft PIX cannot handle the INTZ resource, so you won’t be able to see a preview or the metadata of the INTZ-Surface.

Tags: , , , , , , ,