D3D Occlusion culling
Today, I found this very interesting program when I was surfing on the Internet. Occlusion culling, that is a big topic on Computer Graphic industry.
The original passage come from here: http://www.gamedev.net/page/resources/_/technical/directx-and-xna/occlusion-culling-using-directx-9-r2128.
The main idea as following:
1) Render every objects' bounding mesh, here a full depth map will be created;
2) For every object:
a) Begin query;
b) Re-render the bounding mesh, depth value comparison happen here. Only those pixels that pass though the depth test will be render into the target;
c) End query;
d) Retrieve occlusion query data. If the pixel visible are greater than zero, the objects should be rendered. Otherwise, the object should be occluded from rendering.
The above rendering process will happen on a small render target, not the main scene target. This will save us a bit time, but with some accuracy lost. There is a balance between time and higher accuracy, mainly depend on the final visual effect.
Here are some key lines of code, first to create the occlusion query object:
// Create the query d3dDevice->CreateQuery( D3DQUERYTYPE_OCCLUSION, &d3dQuery );
To do the objects occlusion culling:
HRESULT OcclusionCull() { // Begin occlusionRender if( SUCCEEDED( occlusionRender->BeginScene( occlusionSurface, NULL ) ) ) { // Clear the occlusionRender’s surface d3dDevice->Clear(0, NULL, D3DCLEAR_TARGET|D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB( 200, 200, 200 ), 1.0f, 0); // First, render every object’s bounding box for( int i = 0; i < objects.size(); i++ ) { objects[i].boundingMesh->Render( d3dDevice, objects[i].matTranslate ); } // Now, render each box again, except this time, count how many pixels are visible // by using an occlusion query. We are guaranteed to get the right amount, // since all the bounding boxes have already been rendered for( int i = 0; i < objects.size(); i++ ) { // Start the query d3dQuery->Issue( D3DISSUE_BEGIN ); // Render objects[i].boundingMesh->Render( d3dDevice, objects[i].matTranslate ); // End the query, get the data d3dQuery->Issue( D3DISSUE_END ); // Loop until the data becomes available DWORD pixelsVisible = 0; while (d3dQuery->GetData((void *) &pixelsVisible, sizeof(DWORD), D3DGETDATA_FLUSH) == S_FALSE); if( pixelsVisible == 0 ) objects[i].render = false; // No pixels visible, do not render else objects[i].render = true; // Pixels visible, render } // End the occlusion render scene occlusionRender->EndScene( 0 ); } return S_OK; }
With this method, you could cull some objects effectively. Well, this method may not be suitable for deferred rendering. In deferred rendering, every opaque objects will be rendered only one time. But we could still use it in many different ways: a) If you want to write an out-door scene splitter, this may be a good start. You could figure out which objects are potential visible objects at a specified position. That means you could build a potential visible objects set at a specified zone or sector. This should be done at the step of calculating the game world optimization. b) You could figure out quickly whether some objects are picked by mouse. Here what you need to do is build a mouse pick frustum and set the render target to 1×1 to 2×2 size. Here is a passage about hardware mouse pick with query object: http://www.hindawi.com/journals/ijcgt/2009/730894/.
The full source code could be download from here.