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

Started by
9 comments, last by martinperry 14 years, 1 month ago
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]
Advertisement
I change Texture to A16R16G16B16F and render shadow maps to each channel... i could improve resolution, but problem from screen still appears :(
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.
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.

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
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.vert[0].GetDisplacement( point, nudgeBack );		CPoint v2 = pEdge.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.vert[0].GetDisplacement( point, nudgeBack );		pFace[faceIndex].vert[1] = pCap.vert[1].GetDisplacement( point, nudgeBack );		pFace[faceIndex++].vert[2] = pCap.vert[2].GetDisplacement( point, nudgeBack );		// Add faces for back cap		pFace[faceIndex].vert[0] = pCap.vert[2].GetDisplacement( point, (projectDist * projectDist) + (nudgeBack * 2)  );		pFace[faceIndex].vert[1] = pCap.vert[1].GetDisplacement( point, (projectDist * projectDist) + (nudgeBack * 2)  );		pFace[faceIndex++].vert[2] = pCap.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->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->xVertBuf, 0, sizeof(CPoint) );				CXWindow::Instance().GetXDevice()->SetFVF( D3DFVF_XYZ );				CXWindow::Instance().GetXDevice()->DrawPrimitive( D3DPT_TRIANGLELIST, 0, shadowMeshX->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->xVertBuf, 0, sizeof(CPoint) );			CXWindow::Instance().GetXDevice()->SetFVF( D3DFVF_XYZ );			CXWindow::Instance().GetXDevice()->DrawPrimitive( D3DPT_TRIANGLELIST, 0, shadowMeshX->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.
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.
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.
Portfolio & Blog:http://scgamedev.tumblr.com/
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.
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

This topic is closed to new replies.

Advertisement