Original Post
Hi, I'm trying to implement the SDK shadow volume demo in my own application and...nothing works. :) I'm new to stencil shadows and understand not much at the moment. But I'm trying! Here is what I'm doing and what work's and what not: 1. I render the background of my scene - some cubes - OK 2. I render my player mesh - OK 3. I build with the BuildFromMesh function the shadow volume from my player mesh - ERROR - after some seconds the application crashes. Ok, so I'm doing it once when the application starts and now it is OK 4. I call RenderShadow() - OK 5. I call DrawShadow() - OK .. but after DrawShadow I can still se my player mesh but the whole other scene is black! Simply black. Only the player mesh is drawn like it should. Strange. Here ar the slightly changed SDK sample functions and how I'm using them in my app: BuildFromMesh(): RenderShadow(): DrawShadow(): After that I'm drawing the shadow volume with: pd3dDevice->SetFVF( D3DFVF_XYZ ); pd3dDevice->DrawPrimitiveUP( D3DPT_TRIANGLELIST,m_dwNumVertices/3, m_pVertices, sizeof(D3DXVECTOR3) ); Maybe someone can see the "make-it-all-black"-error? Thanks! Axel
HRESULT BuildFromMesh( LPD3DXMESH pMesh, D3DXVECTOR3 vLight )
{
// Note: the MESHVERTEX format depends on the FVF of the mesh
struct MESHVERTEX { D3DXVECTOR3 p, n; FLOAT tu, tv; };
MESHVERTEX* pVertices;
WORD* pIndices;
// Lock the geometry buffers
pMesh->LockVertexBuffer( 0L, (LPVOID*)&pVertices );
pMesh->LockIndexBuffer( 0L, (LPVOID*)&pIndices );
DWORD dwNumFaces = pMesh->GetNumFaces();
// Allocate a temporary edge list
WORD* pEdges = new WORD[dwNumFaces*[[[6]]]];
if( pEdges == NULL )
{
pMesh->UnlockVertexBuffer();
pMesh->UnlockIndexBuffer();
return E_OUTOFMEMORY;
}
DWORD dwNumEdges = [[[0]]];
// For each face
for( DWORD i=[[[0]]]; i<dwNumFaces; i++ )
{
WORD wFace0 = pIndices[[[[3]]]*i+[[[0]]]];
WORD wFace1 = pIndices[[[[3]]]*i+[[[1]]]];
WORD wFace2 = pIndices[[[[3]]]*i+[[[2]]]];
D3DXVECTOR3 v0 = pVertices[wFace0].p;
D3DXVECTOR3 v1 = pVertices[wFace1].p;
D3DXVECTOR3 v2 = pVertices[wFace2].p;
// Transform vertices or transform light?
D3DXVECTOR3 vCross1(v2-v1);
D3DXVECTOR3 vCross2(v1-v0);
D3DXVECTOR3 vNormal;
D3DXVec3Cross( &vNormal, &vCross1, &vCross2 );
if( D3DXVec3Dot( &vNormal, &vLight ) >= [[[0]]].0f )
{
AddEdge( pEdges, dwNumEdges, wFace0, wFace1 );
AddEdge( pEdges, dwNumEdges, wFace1, wFace2 );
AddEdge( pEdges, dwNumEdges, wFace2, wFace0 );
}
}
for( i=[[[0]]]; i<dwNumEdges; i++ )
{
D3DXVECTOR3 v1 = pVertices[pEdges[[[[2]]]*i+[[[0]]]]].p;
D3DXVECTOR3 v2 = pVertices[pEdges[[[[2]]]*i+[[[1]]]]].p;
D3DXVECTOR3 v3 = v1 - vLight*[[[10]]];
D3DXVECTOR3 v4 = v2 - vLight*[[[10]]];
// Add a quad (two triangles) to the vertex list
m_pVertices[m_dwNumVertices++] = v1;
m_pVertices[m_dwNumVertices++] = v2;
m_pVertices[m_dwNumVertices++] = v3;
m_pVertices[m_dwNumVertices++] = v2;
m_pVertices[m_dwNumVertices++] = v4;
m_pVertices[m_dwNumVertices++] = v3;
}
// Delete the temporary edge list
delete[] pEdges;
// Unlock the geometry buffers
pMesh->UnlockVertexBuffer();
pMesh->UnlockIndexBuffer();
return S_OK;
}
HRESULT RenderShadow()
{
// Disable z-buffer writes (note: z-testing still occurs), and enable the
// stencil-buffer
lpd3d_device->SetRenderState( D3DRS_ZWRITEENABLE, FALSE );
lpd3d_device->SetRenderState( D3DRS_STENCILENABLE, TRUE );
// Dont bother with interpolating color
lpd3d_device->SetRenderState( D3DRS_SHADEMODE, D3DSHADE_FLAT );
// Set up stencil compare fuction, reference value, and masks.
// Stencil test passes if ((ref & mask) cmpfn (stencil & mask)) is true.
// Note: since we set up the stencil-test to always pass, the STENCILFAIL
// renderstate is really not needed.
lpd3d_device->SetRenderState( D3DRS_STENCILFUNC, D3DCMP_ALWAYS );
lpd3d_device->SetRenderState( D3DRS_STENCILZFAIL, D3DSTENCILOP_KEEP );
lpd3d_device->SetRenderState( D3DRS_STENCILFAIL, D3DSTENCILOP_KEEP );
// If ztest passes, inc/decrement stencil buffer value
lpd3d_device->SetRenderState( D3DRS_STENCILREF, 0x1 );
lpd3d_device->SetRenderState( D3DRS_STENCILMASK, 0xffffffff );
lpd3d_device->SetRenderState( D3DRS_STENCILWRITEMASK, 0xffffffff );
lpd3d_device->SetRenderState( D3DRS_STENCILPASS, D3DSTENCILOP_INCR );
// Make sure that no pixels get drawn to the frame buffer
lpd3d_device->SetRenderState( D3DRS_ALPHABLENDENABLE, TRUE );
lpd3d_device->SetRenderState( D3DRS_SRCBLEND, D3DBLEND_ZERO );
lpd3d_device->SetRenderState( D3DRS_DESTBLEND, D3DBLEND_ONE );
// With [[[2]]]-sided stencil, we can avoid rendering twice:
lpd3d_device->SetRenderState( D3DRS_TWOSIDEDSTENCILMODE, TRUE );
lpd3d_device->SetRenderState( D3DRS_CCW_STENCILFUNC, D3DCMP_ALWAYS );
lpd3d_device->SetRenderState( D3DRS_CCW_STENCILZFAIL, D3DSTENCILOP_KEEP );
lpd3d_device->SetRenderState( D3DRS_CCW_STENCILFAIL, D3DSTENCILOP_KEEP );
lpd3d_device->SetRenderState( D3DRS_CCW_STENCILPASS, D3DSTENCILOP_DECR );
lpd3d_device->SetRenderState( D3DRS_CULLMODE, D3DCULL_NONE );
// Draw both sides of shadow volume in stencil/z only
lpd3d_device->SetTransform( D3DTS_WORLD, &m_matObjectMatrix );
RenderShad( lpd3d_device );
lpd3d_device->SetRenderState( D3DRS_TWOSIDEDSTENCILMODE, FALSE );
// Restore render states
lpd3d_device->SetRenderState( D3DRS_SHADEMODE, D3DSHADE_GOURAUD );
lpd3d_device->SetRenderState( D3DRS_CULLMODE, D3DCULL_CCW );
lpd3d_device->SetRenderState( D3DRS_ZWRITEENABLE, TRUE );
lpd3d_device->SetRenderState( D3DRS_STENCILENABLE, FALSE );
lpd3d_device->SetRenderState( D3DRS_ALPHABLENDENABLE, FALSE );
return S_OK;
}
HRESULT DrawShadow()
{
// Set renderstates (disable z-buffering, enable stencil, disable fog, and
// turn on alphablending)
lpd3d_device->SetRenderState( D3DRS_ZENABLE, FALSE );
lpd3d_device->SetRenderState( D3DRS_STENCILENABLE, TRUE );
lpd3d_device->SetRenderState( D3DRS_FOGENABLE, FALSE );
lpd3d_device->SetRenderState( D3DRS_ALPHABLENDENABLE, TRUE );
lpd3d_device->SetRenderState( D3DRS_SRCBLEND, D3DBLEND_SRCALPHA );
lpd3d_device->SetRenderState( D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA );
lpd3d_device->SetTextureStageState( [[[0]]], D3DTSS_COLORARG1, D3DTA_TEXTURE );
lpd3d_device->SetTextureStageState( [[[0]]], D3DTSS_COLORARG2, D3DTA_DIFFUSE );
lpd3d_device->SetTextureStageState( [[[0]]], D3DTSS_COLOROP, D3DTOP_MODULATE );
lpd3d_device->SetTextureStageState( [[[0]]], D3DTSS_ALPHAARG1, D3DTA_TEXTURE );
lpd3d_device->SetTextureStageState( [[[0]]], D3DTSS_ALPHAARG2, D3DTA_DIFFUSE );
lpd3d_device->SetTextureStageState( [[[0]]], D3DTSS_ALPHAOP, D3DTOP_MODULATE );
// Only write where stencil val >= [[[1]]] (count indicates # of shadows that
// overlap that pixel)
lpd3d_device->SetRenderState( D3DRS_STENCILREF, 0x1 );
lpd3d_device->SetRenderState( D3DRS_STENCILFUNC, D3DCMP_LESSEQUAL );
lpd3d_device->SetRenderState( D3DRS_STENCILPASS, D3DSTENCILOP_KEEP );
// Draw a big, gray square
lpd3d_device->SetFVF( SHADOWVERTEX::FVF );
lpd3d_device->SetStreamSource( [[[0]]], m_pBigSquareVB, [[[0]]], sizeof(SHADOWVERTEX) );
lpd3d_device->DrawPrimitive( D3DPT_TRIANGLESTRIP, [[[0]]], [[[2]]] );
// Restore render states
lpd3d_device->SetRenderState( D3DRS_ZENABLE, TRUE );
lpd3d_device->SetRenderState( D3DRS_STENCILENABLE, FALSE );
lpd3d_device->SetRenderState( D3DRS_FOGENABLE, TRUE );
lpd3d_device->SetRenderState( D3DRS_ALPHABLENDENABLE, FALSE );
return S_OK;
}
RenderShad()..this draws the shadow volume:
HRESULT RenderShad( LPDIRECT3DDEVICE9 pd3dDevice )
{
pd3dDevice->SetFVF( D3DFVF_XYZ );
return pd3dDevice->DrawPrimitiveUP( D3DPT_TRIANGLELIST, m_dwNumVertices/[[[3]]],
m_pVertices, sizeof(D3DXVECTOR3) );
}