Occlusion Culling Using DirectX 9
The CodeType DeclarationsSObject (Code Listing 4.1) is the main object entity. CMesh is a class that encapsulates the loading, rendering, and release of an ID3DXMesh interface. struct SObject { CMesh* meshReference; // Reference to a mesh object CMesh* boundingMesh; // Reference to low-poly bounding mesh D3DXVECTOR3 pos; // Position of this object D3DXMATRIX matTranslate; // Translation matrix for this object bool render; // If true, render the object float distanceToCamera; // The distance to the camera (player position) // Constructor SObject( CMesh* meshRef, CMesh* boundMesh, D3DXVECTOR3 position ) { meshReference = meshRef; boundingMesh = boundMesh; pos = position; render = false; distanceToCamera = 0.0f; } }; Code Listing 4.1: SObject definition Object DeclarationFor the occlusion process, interfaces of LPDIRECT3DQUERY9, LPD3DXRENDERTOSURFACE, LPDIRECT3DSURFACE9, and LPDIRECT3DTEXTURE9 need to be declared. LPDIRECT3D9 d3dObject; // Direct3D Object LPDIRECT3DDEVICE9 d3dDevice; // Direct3D Device LPDIRECT3DQUERY9 d3dQuery; // The occlusion query LPD3DXRENDERTOSURFACE occlusionRender; // Occlusion's render to surface LPDIRECT3DSURFACE9 occlusionSurface; // Occlusion's surface that it uses LPDIRECT3DTEXTURE9 occlusionTexture; // Texture to get surface from std::vector<SObject> objects; // Vector of objects Code Listing 4.2: Declarations of objects pertaining to the occlusion culling procedure Setting up the Occlusion ObjectsThe query itself must be created, along with the texture and the render-to-surface. D3DUSAGE_RENDERTARGET is used during the creation of the texture, since it will be rendered to. The surface itself is obtained through the GetSurfaceLevel() function of LPDIRECT3DTEXTURE9. A Z-buffer format of D3DFMT_D16 is used for the LPD3DXRENDERTOSURFACE interface, as it will be needed for use.
//-----------------------------------------------------------------------------
// Name: SetupOcclusion()
// Desc: Create the objects needed for the occlusion culling
//-----------------------------------------------------------------------------
HRESULT SetupOcclusion()
{
// Create the query
d3dDevice->CreateQuery( D3DQUERYTYPE_OCCLUSION, &d3dQuery );
// Get the display mode to obtain the format
D3DDISPLAYMODE mode;
d3dDevice->GetDisplayMode( 0, &mode );
// Create the texture first, so we can get access to it's surface
if( FAILED( D3DXCreateTexture( d3dDevice, 320, 240, 1, D3DUSAGE_RENDERTARGET,
mode.Format, D3DPOOL_DEFAULT, &occlusionTexture ) ) )
{
return E_FAIL;
}
// Obtain the surface (what we really need)
D3DSURFACE_DESC desc;
occlusionTexture->GetSurfaceLevel(0, &occlusionSurface);
occlusionSurface->GetDesc(&desc);
// Create the render to surface
if( FAILED( D3DXCreateRenderToSurface( d3dDevice, desc.Width, desc.Height, desc.Format,
TRUE, D3DFMT_D16, &occlusionRender ) ) )
{
return E_FAIL;
}
return S_OK;
}
Code Listing 4.3: The SetupOcclusion() function
Culling the ObjectsThe OcclusionCull() function implements the theory presented earlier. First, the LPD3DXRENDERTOSURFACE is activated and cleared. Second, every object's bounding mesh is rendered. The meshes are re-rendered and then their occlusion queries are retrieved. Finally, the surface's scene is ended and deactivated.
//-----------------------------------------------------------------------------
// Name: OcclusionCull()
// Desc: Cull the objects
//-----------------------------------------------------------------------------
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 );
// User is pressing the 'M' key, save this buffer to .BMP file
if( keys['M'] )
D3DXSaveSurfaceToFile( "buffer.bmp", D3DXIFF_BMP,
occlusionSurface, NULL, NULL );
}
return S_OK;
}
Code Listing 4.4: The OcclusionCull() function
The Render LoopThe render loop consists of building all the matrices, including the camera and object transforms, then culling. Finally, the objects that will be visible in the final scene are rendered.
//-----------------------------------------------------------------------------
// Name: Render
// Desc: Render a frame
//-----------------------------------------------------------------------------
HRESULT Render()
{
// Setup the matrices for this frame
SetupMatrices();
// Cull the objects
OcclusionCull();
if( SUCCEEDED( d3dDevice->BeginScene() ) )
{
// Clear the main device
d3dDevice->Clear( 0, NULL, D3DCLEAR_TARGET|D3DCLEAR_ZBUFFER,
D3DCOLOR_XRGB(255,0,0), 1.0f, 0 );
// Render the appropriate objects
// Leave out objects that are occluded
for( int i = 0; i < objects.size(); i++ )
{
if( objects[i].render )
{
objects[i].meshReference->Render( d3dDevice, objects[i].matTranslate );
}
}
d3dDevice->EndScene();
}
// Present the scene
d3dDevice->Present( NULL, NULL, NULL, NULL );
return S_OK;
}
Code Listing 4.5: The simplified render loop
|
|