Sign in to follow this  

[UPDATED PROBLEM] Shadows - map contains objects that are "under terrain"

This topic is 2840 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I am really sad... i spend almost month with shadows, but results are still crappy ( Crappy shadow). I use R32F for shadow texture, i pack 3 textures to one my code is inspired with PSSM by nvidia from gpu gems sample... but my result look horrible... my shader: Shader code code for shadow calculation: Shadow calc fragment code (GetFrustrumCorners() worked fine, no bug in that function) Problems i see... first split has different "color", second split is way to much blocky, ugly "ligth" errors in curved terrain if anyone cuold help me, i will be verry pleased (take a quick look on my code and advised me whats wrong)... everything else looks fine in my "engine" but shadow are my nightmare :( [Edited by - martinperry on March 3, 2010 12:03:06 PM]

Share this post


Link to post
Share on other sites
Almost done... i have last bug...



And I can see any errors in my code. If i rendered shadow map to DepthStencilSurface everything worked... now i render it to floating texture and i got this weird bug. I didnt change matrix calculations.

Share this post


Link to post
Share on other sites
All the shadow techniques have their little problems. Looks like you might have some offset issues.

I'm more a fan of shadow volumes. I'll post my code if you want to give that a try. I'm not using shaders for this effect.

Share this post


Link to post
Share on other sites
Quote:
Original post by martinperry
How powerfull shadow volumes are ? I mean.. how much FPS it will cost ? Shadow maps cost me about 100FPS
In my tests, the biggest cost of shadow volumes is the over head of using the stencil buffer and rendering your scene twice. So your fps will vary depending on the video card. For static objects and a light source that doesn't move, you can build your shadow volume during load time and be done with it. For objects that move, animate or a moving light source, you can use a much simpler mesh for those objects.

Below are the relevant snippets of my code. My engine assumes the use of different meshes for the different types of meshes (visual, collision, shadow and so on). Hopefully the below will help and not confuse more.

Below is the different configurations for setting up the stencil buffer

/************************************************************************
* desc: Setup the stencil buffer for shadows. The below is the default
* for two sided stencils
************************************************************************/

void CXWindow::SetStencilBufferForShadows()
{
// general stencil configuration
pDXDevice->SetRenderState( D3DRS_STENCILREF, 0x1 );
pDXDevice->SetRenderState( D3DRS_STENCILMASK, 0xffffffff );
pDXDevice->SetRenderState( D3DRS_STENCILWRITEMASK, 0xffffffff );
pDXDevice->SetRenderState( D3DRS_STENCILFAIL, D3DSTENCILOP_KEEP );
pDXDevice->SetRenderState( D3DRS_CCW_STENCILFAIL, D3DSTENCILOP_KEEP );
pDXDevice->SetRenderState( D3DRS_DESTBLEND, D3DBLEND_ONE );

if( zPassStencilBufferMode )
{
// configure stencil modes for z-pass method
pDXDevice->SetRenderState( D3DRS_STENCILZFAIL, D3DSTENCILOP_KEEP );
pDXDevice->SetRenderState( D3DRS_STENCILPASS, D3DSTENCILOP_INCR );
pDXDevice->SetRenderState( D3DRS_CCW_STENCILZFAIL, D3DSTENCILOP_KEEP );
pDXDevice->SetRenderState( D3DRS_CCW_STENCILPASS, D3DSTENCILOP_DECR );
}
else
{
// configure stencil modes for z-fail method
pDXDevice->SetRenderState( D3DRS_STENCILZFAIL, D3DSTENCILOP_INCR );
pDXDevice->SetRenderState( D3DRS_STENCILPASS, D3DSTENCILOP_KEEP );
pDXDevice->SetRenderState( D3DRS_CCW_STENCILZFAIL, D3DSTENCILOP_DECR );
pDXDevice->SetRenderState( D3DRS_CCW_STENCILPASS, D3DSTENCILOP_KEEP );
}
} // SetStencilBufferForShadows

My code is very modular so not everything is represented because I'm calling other objects. This is it in a nut shell

1) Use the plain equation to determine if the triangle is facing the light source point. Store all these faces for creating the caps.
2) Find the unique edges of the face to find the outline.
3) Build the quad mesh volume from the found edges.

The Z-Pass method is for if your camera doesn't enter the shadow volume and the Z-Fail method is for if it does. The Z-Fail method requires capping of your shadow volume but so does Z-Pass if your shadow mesh is different from your visual mesh. Capping Z-Pass may not be required but does solves odd display problems.

The code I posted shows how to handle single and double pass stencil methods.

/************************************************************************
* desc: Build the shadow mesh
*
* param: CPoint & point - point of the light
************************************************************************/

void CShadowSprite::Build( CPoint & point )
{
edgeCounter = 0;
capCounter = 0;

for( unsigned int face = 0; face < pMesh->GetFaceCount(); face++ )
{
// Check if the light point is facing plane of the face
if( pTransFace[face].IsFacingPlane( point ) )
{
// Try to add any of these face edges to the list
AddFaceEdge( pTransFace[face] );

// Add the face for building the caps
AddFaceToCap( pTransFace[face] );
}
}

// Project the edges to create the directX mesh
CreateDirectXMesh( point );

} // Build


/************************************************************************
* desc: Add edges from this face
*
* param: CFace & face - reference to face to see if we can add any of
* these edges to the list
************************************************************************/

void CShadowSprite::AddFaceEdge( CFace & face )
{
for( int i = 0; i < 3; i++ )
{
CEdge tmpEdge;
bool found = false;

// Get the edge from the face
face.GetEdge( i, tmpEdge );

// go through all the edges until we find a match
for( unsigned int j = 0; j < edgeCounter; j++ )
{
// If we found an edge, remove it from the vector edge list
if( pEdge[j] == tmpEdge )
{
found = true;

// copy the last edge into the deleted edge's spot
pEdge[j] = pEdge[--edgeCounter];

break;
}
}

// If we didn't find this edge, add it in.
if( !found )
{
assert(edgeCounter < (pMesh->GetFaceCount() >> 1));
pEdge[edgeCounter++] = tmpEdge;
}
}

} // AddFaceEdge


/************************************************************************
* desc: Add the face for building the caps
*
* param: CFace & face - reference to face to see if we can add any of
* these edges to the list
************************************************************************/

void CShadowSprite::AddFaceToCap( CFace & face )
{
assert(capCounter < pMesh->GetFaceCount() );

// Copy over face
pCap[capCounter].vert[0] = face.vert[0];
pCap[capCounter].vert[1] = face.vert[1];
pCap[capCounter].vert[2] = face.vert[2];

capCounter++;

} // AddFaceToCap


/************************************************************************
* desc: Create the DirectX Mesh
*
* param: CPoint & point - light position
************************************************************************/

bool CShadowSprite::CreateDirectXMesh( CPoint & point )
{
CXVertShadowBuff * pShadowBuf = new CXVertShadowBuff;
float nudgeBack = 0.02f;

// Each edge represents a quad
pShadowBuf->fcount = (edgeCounter * 2) + (capCounter * 2);

// create the vertex buffer
if( D3D_OK != CXWindow::Instance().GetXDevice()->CreateVertexBuffer( pShadowBuf->fcount * sizeof(CXShadowFace),
D3DUSAGE_WRITEONLY,
D3DFVF_XYZ,
D3DPOOL_MANAGED,
&pShadowBuf->xVertBuf,
NULL ) )
{
PostMsg( "Init Error",
"Error creating vertex shadow buffer. Your system resources may be low so try restarting your computer." );

return false;
}

// Make a face pointer
CXShadowFace * pFace;

// Lock the vertex buffer for copying
pShadowBuf->xVertBuf->Lock( 0, 0, (void **)&pFace, 0 );

// Use each edge to build two triangles
for( unsigned int i = 0; i < edgeCounter; i++ )
{
CPoint v1 = pEdge[i].vert[0].GetDisplacement( point, nudgeBack );
CPoint v2 = pEdge[i].vert[1].GetDisplacement( point, nudgeBack );

CPoint v3 = v1.GetDisplacement( point, (projectDist * projectDist) + nudgeBack );
CPoint v4 = v2.GetDisplacement( point, (projectDist * projectDist) + nudgeBack );

// First triangle
pFace[i*2+0].vert[0] = v3;
pFace[i*2+0].vert[1] = v2;
pFace[i*2+0].vert[2] = v1;

// second triangle
pFace[i*2+1].vert[0] = v3;
pFace[i*2+1].vert[1] = v4;
pFace[i*2+1].vert[2] = v2;
}

int faceIndex = edgeCounter * 2;
for( unsigned int i = 0; i < capCounter; i++ )
{
// Add faces for the front cap
pFace[faceIndex].vert[0] = pCap[i].vert[0].GetDisplacement( point, nudgeBack );
pFace[faceIndex].vert[1] = pCap[i].vert[1].GetDisplacement( point, nudgeBack );
pFace[faceIndex++].vert[2] = pCap[i].vert[2].GetDisplacement( point, nudgeBack );

// Add faces for back cap
pFace[faceIndex].vert[0] = pCap[i].vert[2].GetDisplacement( point, (projectDist * projectDist) + (nudgeBack * 2) );
pFace[faceIndex].vert[1] = pCap[i].vert[1].GetDisplacement( point, (projectDist * projectDist) + (nudgeBack * 2) );
pFace[faceIndex++].vert[2] = pCap[i].vert[0].GetDisplacement( point, (projectDist * projectDist) + (nudgeBack * 2) );
}

// Unlock the buffer so it can be used
pShadowBuf->xVertBuf->Unlock();

// project the shadow end center of radius
pShadowBuf->center = trans_center.GetDisplacement( point, projectDist * projectDist );

// Add to our vector
shadowMeshX.push_back( pShadowBuf );

return true;

} // CreateDirectXMesh


/************************************************************************
* desc: Construct the shadow and render it
*
* param: CMatric & matrix - passed in matrix class
************************************************************************/

void CShadowSprite::Render( CMatrix & matrix )
{
// Do our translations and rotations in a temporary matrix
CMatrix tmpMatrix;

// Add in the scaling
tmpMatrix.Scale( scale );

// Set to the temporary matrix to world view
SetMatrixToWorldView( tmpMatrix );

// Merge in the passed in matrix to convert to camera view
tmpMatrix.MergeMatrix( matrix );

// Transform the center point - needed for CullMesh_BoundSphere()
Transform( tmpMatrix );

// Copy it to the DirectX matrix
D3DXMATRIX xMatrix( matrix.GetMatrix() );

// Set the world view transformation to DirectX
CXWindow::Instance().GetXDevice()->SetTransform( D3DTS_VIEW, &xMatrix ); // D3DTS_WORLD D3DTS_VIEW

// See if we can cull the front center radius of the shadow volume
int frontCullValue = CullMesh_BoundSphere( trans_center );
if( frontCullValue == 0 )
{
frontCullValue = -1;
}

// Render the mesh
for( unsigned int i = 0; i < shadowMeshX.size(); i++ )
{
CPoint trans_shadowEnd;
tmpMatrix.Transform( trans_shadowEnd, shadowMeshX[i]->center );

int backCullValue = CullMesh_BoundSphere( trans_shadowEnd );

// Test if is shadow is within the view frustum
if( frontCullValue != backCullValue )
{
// Inc our stat counter to keep track of what is going on.
CStatCounter::Instance().IncShadowCounter();

// Use the two sided stencil for shadows
if( CXWindow::Instance().IsTwoSidedStencil() )
{
CXWindow::Instance().GetXDevice()->SetRenderState( D3DRS_CULLMODE, D3DCULL_NONE );
CXWindow::Instance().GetXDevice()->SetRenderState( D3DRS_TWOSIDEDSTENCILMODE, true );
}
else // Do traiditional two pass shadow stencil*/
{
// render back faces
CXWindow::Instance().GetXDevice()->SetRenderState( D3DRS_CULLMODE, D3DCULL_CW );

if( CXWindow::Instance().IsUsingZPassShadowMethod() )
{
CXWindow::Instance().GetXDevice()->SetRenderState( D3DRS_STENCILPASS, D3DSTENCILOP_INCR );
}
else
{
CXWindow::Instance().GetXDevice()->SetRenderState( D3DRS_STENCILZFAIL, D3DSTENCILOP_DECR );
}

// Render the shadow
CXWindow::Instance().GetXDevice()->SetStreamSource( 0, shadowMeshX[i]->xVertBuf, 0, sizeof(CPoint) );
CXWindow::Instance().GetXDevice()->SetFVF( D3DFVF_XYZ );
CXWindow::Instance().GetXDevice()->DrawPrimitive( D3DPT_TRIANGLELIST, 0, shadowMeshX[i]->fcount );

// render front faces
CXWindow::Instance().GetXDevice()->SetRenderState( D3DRS_CULLMODE, D3DCULL_CCW );

if( CXWindow::Instance().IsUsingZPassShadowMethod() )
{
CXWindow::Instance().GetXDevice()->SetRenderState( D3DRS_STENCILPASS, D3DSTENCILOP_DECR );
}
else
{
CXWindow::Instance().GetXDevice()->SetRenderState( D3DRS_STENCILZFAIL, D3DSTENCILOP_INCR );
}
}

// Render the shadow
CXWindow::Instance().GetXDevice()->SetStreamSource( 0, shadowMeshX[i]->xVertBuf, 0, sizeof(CPoint) );
CXWindow::Instance().GetXDevice()->SetFVF( D3DFVF_XYZ );
CXWindow::Instance().GetXDevice()->DrawPrimitive( D3DPT_TRIANGLELIST, 0, shadowMeshX[i]->fcount );
}
}

// Turn off two sided stencil mode if enambled
CXWindow::Instance().GetXDevice()->SetRenderState( D3DRS_TWOSIDEDSTENCILMODE, false );

// Reset the cull mode
CXWindow::Instance().GetXDevice()->SetRenderState( D3DRS_CULLMODE, D3DCULL_CCW );

} // Render

... and this is how you render it.

/***************************************************************************
* decs: Render the objects to the screen
****************************************************************************/

void CGame::Render()
{
// Calc the finialized camera position
camera.Finialize();

if( lightLst.IsShadowCast() )
{
lightLst.Enable( string("point"), false );

// Turn off alpha blending
CXWindow::Instance().GetXDevice()->SetRenderState( D3DRS_ALPHABLENDENABLE, false );

// ambient lighting ON
CXWindow::Instance().GetXDevice()->SetRenderState( D3DRS_AMBIENT, D3DCOLOR_RGBA(120,120,120,0) );

// Render visual meshes
column.Render( camera.GetFinialMatrix(), lightLst );
column2.Render( camera.GetFinialMatrix(), lightLst );
column3.Render( camera.GetFinialMatrix(), lightLst );
column4.Render( camera.GetFinialMatrix(), lightLst );
knaus.Render( camera.GetFinialMatrix(), lightLst );
environment.Render( camera.GetFinialMatrix(), lightLst, false );

// turn off the z-buffer
CXWindow::Instance().GetXDevice()->SetRenderState( D3DRS_ZWRITEENABLE, false );

// turn OFF colour buffer
CXWindow::Instance().GetXDevice()->SetRenderState( D3DRS_ALPHABLENDENABLE, true );

// disable lighting (not needed for stencil writes!)
CXWindow::Instance().GetXDevice()->SetRenderState( D3DRS_LIGHTING, false );

// turn ON stencil buffer
CXWindow::Instance().GetXDevice()->SetRenderState( D3DRS_STENCILENABLE, true );

// Setup for shadow blending
CXWindow::Instance().GetXDevice()->SetRenderState( D3DRS_SRCBLEND, D3DBLEND_ZERO );
CXWindow::Instance().GetXDevice()->SetRenderState( D3DRS_STENCILFUNC, D3DCMP_ALWAYS );

// Render shadow volumes
columnShadow.Render( camera.GetFinialMatrix() );
columnShadow2.Render( camera.GetFinialMatrix() );
columnShadow3.Render( camera.GetFinialMatrix() );
columnShadow4.Render( camera.GetFinialMatrix() );
knaus_shadow.UpdateAnimation( lightLst );
knaus_shadow.Render( camera.GetFinialMatrix() );

lightLst.Enable( string("point"), true );

// turn ON the z-buffer
CXWindow::Instance().GetXDevice()->SetRenderState( D3DRS_ZWRITEENABLE, true );

// turn ON colour buffer
CXWindow::Instance().GetXDevice()->SetRenderState( D3DRS_ALPHABLENDENABLE, true );

// Setup to blend the rest of the scene
CXWindow::Instance().GetXDevice()->SetRenderState( D3DRS_SRCBLEND, D3DBLEND_ONE );

if( CXWindow::Instance().IsUsingZPassShadowMethod() )
{
CXWindow::Instance().GetXDevice()->SetRenderState( D3DRS_STENCILFUNC, D3DCMP_GREATER );
}
else
{
CXWindow::Instance().GetXDevice()->SetRenderState( D3DRS_STENCILFUNC, D3DCMP_GREATEREQUAL );
}

// turn on CURRENT light
CXWindow::Instance().GetXDevice()->SetRenderState( D3DRS_LIGHTING, true );
CXWindow::Instance().GetXDevice()->SetRenderState( D3DRS_AMBIENT, D3DCOLOR_RGBA(0,0,0,0) );
}

column.Render( camera.GetFinialMatrix(), lightLst, true );
column2.Render( camera.GetFinialMatrix(), lightLst, true );
column3.Render( camera.GetFinialMatrix(), lightLst, true );
column4.Render( camera.GetFinialMatrix(), lightLst, true );

environment.Render( camera.GetFinialMatrix(), lightLst, false );

CXWindow::Instance().GetXDevice()->SetRenderState( D3DRS_STENCILENABLE, false );

knaus.Render( camera.GetFinialMatrix(), lightLst, true );

} // Render

Hopefully this will help. There are many tutorials online so that should help fill in any gaps.

Share this post


Link to post
Share on other sites
Thank you very much... i will look at it and try to implement it.

However. Stencil shadow (as i see it from implementation) are better for objects in scene (not for large terrain) and for point lights like torches (not for sun)...

but thanks anyway... i am currently doing some sort of Dungeon game, so there will be it very usefull. Shadow maps are dead end in corridors or indor scenes.

Share this post


Link to post
Share on other sites
No offence meant to howie but if you are going to learn and perfect any shadowing technique then for your own benefit learn shadow maps properly. With the power of GPUs nowadays and the amount of texture bandwidth available shadow maps are quick and a lot, lot easier to learn, implement and make look good when compared to stencil maps.

Stencil maps are rarely used anymore for shadows in games, if you look at the likes of crysis and other FPS games it's almost always shadow maps. It's just too easy for stencil maps to go wrong and shadow buffers, while not perfectly accurate by any means are more "real" then stencil maps.

Share this post


Link to post
Share on other sites
Quote:
Original post by martinperry
Hm... you can post your code, if you want...

How powerfull shadow volumes are ? I mean.. how much FPS it will cost ? Shadow maps cost me about 100FPS


That doesn't actually say anything at all.
Dropping from 500 fps to 400 fps is a smaller drop than 400 to 300 fps.
They are both a drop of "100 fps", but what does that really tell you?

500 fps: 2 spf
400 fps: 2.5 spf + 0.5 ms per frame
300 fps: 3.34 spf + 0.84 ms per frame

In one case you're using 0.5 milliseconds more than before, and you lose 100 frames per second.
The next case, you're spending 0.84 milliseconds more per frame, but your frame-loss is still 100.

Had the computations been equally expensive, then the second loss would have given you a FPS of 333.34, not 300.
Try using your shadow technique twice per frame, you will lose 166.67 frames per second, instead of 200.

You might want to look into more accurate ways of measuring how heavy the computation is.

Share this post


Link to post
Share on other sites
Quote:

Stencil maps are rarely used anymore for shadows in games, if you look at the likes of crysis and other FPS games it's almost always shadow maps. It's just too easy for stencil maps to go wrong and shadow buffers, while not perfectly accurate by any means are more "real" then stencil maps.


But shadow maps work not very good in indoor scenes with spot lights, is it ?

However... i asked several people about my problem and nobody can give me any advice...
I tried already chanfe texScaleMatrix, add offsets manually direct to shader (that in some way worked, but if i positioned first cascade, the others were still out of position.. and even more)

I am really out of ideas

Share this post


Link to post
Share on other sites
Ok... I found bug (at least i think so). Problem is, that i render to texture even things, that are "under ground" (eg. not visible). I need to enable depth stencil to correct this errors, but for now i dont know how to solve this.

Share this post


Link to post
Share on other sites

This topic is 2840 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this