[UPDATED PROBLEM] Shadows - map contains objects that are "under terrain"
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]
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.
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.
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
How powerfull shadow volumes are ? I mean.. how much FPS it will cost ? Shadow maps cost me about 100FPS
Quote:Original post by martinperryIn 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.
How powerfull shadow volumes are ? I mean.. how much FPS it will cost ? Shadow maps cost me about 100FPS
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.
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.
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.
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
Popular Topics
Advertisement