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 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.
Hi Sebastian.
Just saw your note about z-buffer reading on nvidia while doing my fast research. Im trying to find out if it is possible on nvidia(or any other) cards to monitor a state of z-buffer for some 3d game like skyrim or quake3(anything title would do). basically i would like to get a z-buffer read for every frame or something like that. do you have any knowledge/links/advices on if this is even remotely possible for any card/game combination or do you think it is totally impossible? Thanks for your time!
Jarek Kochanowicz(jarek108@gmail.com)
LikeLike