Z Pass to Z Fail - Capping shadow volumes
Started by shadowyogin, Sep 11 2003 09:28 AM
19 replies to this topic
#1 Members - Reputation: 122
Posted 11 September 2003 - 09:28 AM
I have been working with the Z Pass shadow volume method demonstrated at codesampler.com for some time. It has been a good solution for me up until a few days ago when I tried to move my camera through a shadow volume (Evil music plays) which resulted in inverse shadows or as Kwoon says, “All hell breaks loose.”
Frantic research on Google and intense study of Hun Yen Kwoon’s Theory of Stencil Shadow Volumes has led me to understand that this is a near plane clipping problem often solved by implementing the Z Fail algorithm (Carmack’s Reverse). This method involves capping the shadow volume.
The questions I am going to ask are about a specific application. Screen shots and the source can be downloaded at: http://www.neke.net/neke_forumfiles.html
In this source code we are building a shadow volume that extends to point determined by this line of code.
// CShadowVolume.cpp
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*5;
D3DXVECTOR3 v4 = v2 - vLight*5;
…
}
The bit that extends each edge is v1 – vLight * 5; Where *5 determines the length of the volume.
So v1 and v2 are the near vertices and v3 and v4 are the far vertices of the volume.
Question 1:
Do I need to add a cap to the end of this volume for Z Fail even if it is finite?
The answer is probably yes but I had to start with an easy one.
Question 2:
What is the easiest solution to capping the volume with this method?
A generic algorithm would help.
Question 3:
Does anyone know of an implementation of the Z-Fail with DX 8 – 9?
I have more questions but I will wait for these to be answered first.
Sponsor:
#2 Members - Reputation: 122
Posted 11 September 2003 - 09:49 AM
1. yes
2. you simply add the front-facing polygons non-extruded, and the back facing polygons extruded (dot product with the light vector). you can also just extrude the front facing polygons as well (instead of the back ones), as long as it caps the volume.
3. once you have the capping working correctly, if you just follow carmacks notes (there on nvidia if you dont know of them) on the stenciled shadows, its a fairly easy conversion from z-pass to z-fail shadows. if it 'kinda' works its most likely because you dont have capping 100% correct.
theres a shadow volume article on this site that may be of some very good use to you as well.
regards,
-Eric
[edited by - Vulcan on September 11, 2003 4:51:10 PM]
2. you simply add the front-facing polygons non-extruded, and the back facing polygons extruded (dot product with the light vector). you can also just extrude the front facing polygons as well (instead of the back ones), as long as it caps the volume.
3. once you have the capping working correctly, if you just follow carmacks notes (there on nvidia if you dont know of them) on the stenciled shadows, its a fairly easy conversion from z-pass to z-fail shadows. if it 'kinda' works its most likely because you dont have capping 100% correct.
theres a shadow volume article on this site that may be of some very good use to you as well.
regards,
-Eric
[edited by - Vulcan on September 11, 2003 4:51:10 PM]
#4 Members - Reputation: 122
Posted 11 September 2003 - 12:03 PM
when you go through the polygons of your mesh, for each face you determine whether it is a back-facing or front facing polygon to the light source (take the dot product with the surface normal to the light vector). if its a back face, extrude the vertices to the edge of your volume and add them to your volume, if its a front face, just add them to the volume without extruding.
ill also direct you here as it is one of the better articles on the subject imo.
regards,
-Eric
ill also direct you here as it is one of the better articles on the subject imo.
regards,
-Eric
#6 Members - Reputation: 1360
Posted 11 September 2003 - 02:29 PM
Here is an important link to info of Z-Fail shadow volumes: Practical and Robust Shadow Volumes
#7 Members - Reputation: 174
Posted 11 September 2003 - 04:04 PM
if( D3DXVec3Dot( &vNormal, &vLight ) <= 0.0f )
{
addEdge( pEdges, dwNumEdges, wFace1, wFace0 );
addEdge( pEdges, dwNumEdges, wFace2, wFace1 );
addEdge( pEdges, dwNumEdges, wFace0, wFace2 );
//add the light facing triangles to the Shadow volume to form the front cap
m_pVertices[m_dwNumVertices++]=v0;
m_pVertices[m_dwNumVertices++]=v1;
m_pVertices[m_dwNumVertices++]=v2;
/*add the light front facing triangles to the Shadow volume to form the back cap after reversing their order and extending them in light direction */
m_pVertices[m_dwNumVertices++] = v2-vLight*10;
m_pVertices[m_dwNumVertices++] = v1-vLight*10;
m_pVertices[m_dwNumVertices++] = v0-vLight*10;
}
}
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;
}
This is the method for the generation of the front cap and back cap.the problem is in the z fight between the shadow volume polygons and the model polygons.This will make the whole model to be shadowed even light facing polygons.you have one of two solutions:
1-to move the vertecies of the front cap slightly inwards in the direction of the normal of the vertices and this is the most robust and efficient solutilion I think.( found in nvidia papers)
2-to use the back facing polygons as the front cap after reversing their normal.this will be a good solution if you are subtracting the diffuse lighting value for the pixels in shadow but if you are using a gray square on the screen it won''t give you the right result. this can have the following code:
if( D3DXVec3Dot( &vNormal, &vLight ) >= 0.0f )
{
addEdge( pEdges, dwNumEdges, wFace0, wFace1 );
addEdge( pEdges, dwNumEdges, wFace1, wFace2 );
addEdge( pEdges, dwNumEdges, wFace2, wFace0 );
/*add the light back facing triangles to the Shadow volume to form the front cap after reversig their order*/
m_pVertices[m_dwNumVertices++] = v2;
m_pVertices[m_dwNumVertices++] = v1;
m_pVertices[m_dwNumVertices++] = v0;
/* use light back facing triangles and extend them in the light direction to form the back cap */
m_pVertices[m_dwNumVertices++] = v0-vLight*10;
m_pVertices[m_dwNumVertices++] = v1-vLight*10;
m_pVertices[m_dwNumVertices++] = v2-vLight*10;
}
}
{
addEdge( pEdges, dwNumEdges, wFace1, wFace0 );
addEdge( pEdges, dwNumEdges, wFace2, wFace1 );
addEdge( pEdges, dwNumEdges, wFace0, wFace2 );
//add the light facing triangles to the Shadow volume to form the front cap
m_pVertices[m_dwNumVertices++]=v0;
m_pVertices[m_dwNumVertices++]=v1;
m_pVertices[m_dwNumVertices++]=v2;
/*add the light front facing triangles to the Shadow volume to form the back cap after reversing their order and extending them in light direction */
m_pVertices[m_dwNumVertices++] = v2-vLight*10;
m_pVertices[m_dwNumVertices++] = v1-vLight*10;
m_pVertices[m_dwNumVertices++] = v0-vLight*10;
}
}
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;
}
This is the method for the generation of the front cap and back cap.the problem is in the z fight between the shadow volume polygons and the model polygons.This will make the whole model to be shadowed even light facing polygons.you have one of two solutions:
1-to move the vertecies of the front cap slightly inwards in the direction of the normal of the vertices and this is the most robust and efficient solutilion I think.( found in nvidia papers)
2-to use the back facing polygons as the front cap after reversing their normal.this will be a good solution if you are subtracting the diffuse lighting value for the pixels in shadow but if you are using a gray square on the screen it won''t give you the right result. this can have the following code:
if( D3DXVec3Dot( &vNormal, &vLight ) >= 0.0f )
{
addEdge( pEdges, dwNumEdges, wFace0, wFace1 );
addEdge( pEdges, dwNumEdges, wFace1, wFace2 );
addEdge( pEdges, dwNumEdges, wFace2, wFace0 );
/*add the light back facing triangles to the Shadow volume to form the front cap after reversig their order*/
m_pVertices[m_dwNumVertices++] = v2;
m_pVertices[m_dwNumVertices++] = v1;
m_pVertices[m_dwNumVertices++] = v0;
/* use light back facing triangles and extend them in the light direction to form the back cap */
m_pVertices[m_dwNumVertices++] = v0-vLight*10;
m_pVertices[m_dwNumVertices++] = v1-vLight*10;
m_pVertices[m_dwNumVertices++] = v2-vLight*10;
}
}
#8 Members - Reputation: 163
Posted 11 September 2003 - 11:32 PM
There are a couple speedups that can be easily understood and REALLY help out your frame rate, as Carmack''s Reverse isn''t [nearly] as fast as the Z-Pass method as you have roughly triple the number of polygons in Z-Fail as Z-Pass. Therefore, you want to use Z-Pass whenever possible, or at least reduce the # of polygons when using Z-Fail. The first trick, determining when to use Z-Pass or -Fail.
1) When I render my shadow volumes, I always perform a simple test. If a ray (a vector with an origin) can pass from the camera''s current position to the light without passing through the shadowing objects bounding box, then you''re DEFINATELY not ''in'' the shadow. If it DOES pass through the bounding box, then you MIGHT be in the shadow (considering the case of a large bounding box when the object is small). You can take the next part as far as you like. You can intersect directly with the mesh to see if you DEFINATELY are in the shadow, or you can use a bounding box heirarchy to reduce the possible error of being in the shadow. In any case, if you determine you''re DEFINATELY not in the shadow, use Z-Pass, otherwise, use Z-Fail.
2) If you HAVE to use Z-Fail (for any reason, including that of being in the shadow), you need to try to reduce the number of polygons you need to draw (and adding the caps on the volume considerably ups the poly count, though this depends on your mesh, of course). If you find yourself with a huge FPS drop when you enter the shadow (aka use Z-Fail), do what I do: build a plane going through the camera''s current position with the normal being LightPos - CameraPos. Then, test the camera''s current view direction against this plane (to determine on which side of the plane the camera is currently viewing). The logic behind this is that you can RARELY see both ends of the volume at the same time. The only case I''ve come across is when the distance between the shadow-casting object and the shadow-receiving object (e.g. a wall) is very small (compared to the current Field of View) that you CAN in fact, see both ends. To detect this, the only way I can think of would be to determine the angle between the plane normal and the view direction. If it''s between, say 85 - 95 degrees, add both caps to the volume (you may want to adjust the 85 - 95 degree range depending on your Field of View, though I can''t think of a way to do this). In the end, it comes down to:
- Determine if in Shadow Volume
- No? Use Z-Pass
- Yes?
- construct plane with normal parallel to light-camera difference
- test view direction against this plane.
- If facing light, add front-facing polygon cap to shadow volume.
- If facing away from light, add extruded back-facing polygon cap to shadow volume.
- If unsure of viewer direction (e.g. can PERHAPS see both ends), add both caps to volume (better safe then ugly)
Hope these help you when you get the implementation going, my implementation really helped out my framerate.
Chris Pergrossi
My Realm | "Good Morning, Dave"
1) When I render my shadow volumes, I always perform a simple test. If a ray (a vector with an origin) can pass from the camera''s current position to the light without passing through the shadowing objects bounding box, then you''re DEFINATELY not ''in'' the shadow. If it DOES pass through the bounding box, then you MIGHT be in the shadow (considering the case of a large bounding box when the object is small). You can take the next part as far as you like. You can intersect directly with the mesh to see if you DEFINATELY are in the shadow, or you can use a bounding box heirarchy to reduce the possible error of being in the shadow. In any case, if you determine you''re DEFINATELY not in the shadow, use Z-Pass, otherwise, use Z-Fail.
2) If you HAVE to use Z-Fail (for any reason, including that of being in the shadow), you need to try to reduce the number of polygons you need to draw (and adding the caps on the volume considerably ups the poly count, though this depends on your mesh, of course). If you find yourself with a huge FPS drop when you enter the shadow (aka use Z-Fail), do what I do: build a plane going through the camera''s current position with the normal being LightPos - CameraPos. Then, test the camera''s current view direction against this plane (to determine on which side of the plane the camera is currently viewing). The logic behind this is that you can RARELY see both ends of the volume at the same time. The only case I''ve come across is when the distance between the shadow-casting object and the shadow-receiving object (e.g. a wall) is very small (compared to the current Field of View) that you CAN in fact, see both ends. To detect this, the only way I can think of would be to determine the angle between the plane normal and the view direction. If it''s between, say 85 - 95 degrees, add both caps to the volume (you may want to adjust the 85 - 95 degree range depending on your Field of View, though I can''t think of a way to do this). In the end, it comes down to:
- Determine if in Shadow Volume
- No? Use Z-Pass
- Yes?
- construct plane with normal parallel to light-camera difference
- test view direction against this plane.
- If facing light, add front-facing polygon cap to shadow volume.
- If facing away from light, add extruded back-facing polygon cap to shadow volume.
- If unsure of viewer direction (e.g. can PERHAPS see both ends), add both caps to volume (better safe then ugly)
Hope these help you when you get the implementation going, my implementation really helped out my framerate.
Chris Pergrossi
My Realm | "Good Morning, Dave"
#9 Members - Reputation: 122
Posted 12 September 2003 - 06:11 AM
Thanks to everyone for the help.
As soon as I get the implementation up I will try out the algorithm suggested by c t o a n.
But unitl then...
I'm at the part now where I have the capping working correctly (thanks to mohamed
) and I am now dealing with the coplaner Z-fight between the occluder and the shadow volume cap.
Hun Yen Kwoon says that we can make use of the D3DRS_ZBIAS flag when setting the render state to fix this problem. We set the value of the occluder to a higher number than the shadow volume.
Well DirectX 9 doesn't have D3DRS_ZBIAS anymore. I think it's called D3DRS_DEPTHBIAS. This is what I tried.
g_pd3dDevice->SetRenderState(D3DRS_DEPTHBIAS, 1);
m_Occluder.Render();
g_pd3dDevice->SetRenderState(D3DRS_DEPTHBIAS, 0);
renderShadowToStencilBuffer();
renderShadowToScene();
But it does not seem to work at all.?
I should also mention that I am still using the Z-Pass algorithm with the capped volume.
_Neil
[edited by - shadowyogin on September 12, 2003 1:21:39 PM]
As soon as I get the implementation up I will try out the algorithm suggested by c t o a n.
But unitl then...
I'm at the part now where I have the capping working correctly (thanks to mohamed
Hun Yen Kwoon says that we can make use of the D3DRS_ZBIAS flag when setting the render state to fix this problem. We set the value of the occluder to a higher number than the shadow volume.
Well DirectX 9 doesn't have D3DRS_ZBIAS anymore. I think it's called D3DRS_DEPTHBIAS. This is what I tried.
g_pd3dDevice->SetRenderState(D3DRS_DEPTHBIAS, 1);
m_Occluder.Render();
g_pd3dDevice->SetRenderState(D3DRS_DEPTHBIAS, 0);
renderShadowToStencilBuffer();
renderShadowToScene();
But it does not seem to work at all.?
I should also mention that I am still using the Z-Pass algorithm with the capped volume.
_Neil
[edited by - shadowyogin on September 12, 2003 1:21:39 PM]
#11 Members - Reputation: 163
Posted 12 September 2003 - 09:43 PM
You should only have to push back the front-cap, ya? The back cap can be wherever you want it to be (though you need to watch out if you want to force it all the way to infinity, as that is ALOT of fill space if there''s nothing blocking the shadow...
Chris Pergrossi
My Realm | "Good Morning, Dave"
Chris Pergrossi
My Realm | "Good Morning, Dave"
#12 Members - Reputation: 174
Posted 13 September 2003 - 10:54 AM
zBias does not work with the shadow volume ,it gives extremely wrong results and I asked the same question you asked in the directx dev mailing list and I had the answer of pushing the normals of the front cap in the opposite direction of the normals and I implemented it and it worked well but the z fight will appear at very great z values due to the decrease of z presision with the high depth,but as I said before using the light back facing polygons will not let this happen.here you are the new technique (only very small modification) :
if( D3DXVec3Dot( &vNormal, &vLight ) >= 0.0f )
{
addEdge( pEdges, dwNumEdges, wFace0, wFace1 );
addEdge( pEdges, dwNumEdges, wFace1, wFace2 );
addEdge( pEdges, dwNumEdges, wFace2, wFace0 );
//add the light back facing triangles to the Shadow volume to form the front cap
m_pVertices[m_dwNumVertices++] = v2-pVertices[wFace2].n*0.003;
m_pVertices[m_dwNumVertices++] = v1-pVertices[wFace1].n*0.003;
m_pVertices[m_dwNumVertices++] = v0-pVertices[wFace0].n*0.003;
//Reverse the normals for light back facing triangles and extend them in the light direction to form
//the back cap of the shadow volume
m_pVertices[m_dwNumVertices++] = v0-vLight*10;
m_pVertices[m_dwNumVertices++] = v1-vLight*10;
m_pVertices[m_dwNumVertices++] = v2-vLight*10;
}
I hope this would help
[edited by - mohamed adel on September 13, 2003 5:56:27 PM]
if( D3DXVec3Dot( &vNormal, &vLight ) >= 0.0f )
{
addEdge( pEdges, dwNumEdges, wFace0, wFace1 );
addEdge( pEdges, dwNumEdges, wFace1, wFace2 );
addEdge( pEdges, dwNumEdges, wFace2, wFace0 );
//add the light back facing triangles to the Shadow volume to form the front cap
m_pVertices[m_dwNumVertices++] = v2-pVertices[wFace2].n*0.003;
m_pVertices[m_dwNumVertices++] = v1-pVertices[wFace1].n*0.003;
m_pVertices[m_dwNumVertices++] = v0-pVertices[wFace0].n*0.003;
//Reverse the normals for light back facing triangles and extend them in the light direction to form
//the back cap of the shadow volume
m_pVertices[m_dwNumVertices++] = v0-vLight*10;
m_pVertices[m_dwNumVertices++] = v1-vLight*10;
m_pVertices[m_dwNumVertices++] = v2-vLight*10;
}
I hope this would help
[edited by - mohamed adel on September 13, 2003 5:56:27 PM]
#13 Members - Reputation: 1102
Posted 14 September 2003 - 12:31 PM
I had the Z problem between the occluder and the front cap. I tried the Z depth bias as you mentioned and gave up after getting really strange results. I then tried setting ZBufferFunction = Compare.Less (the default is LessEqual), and it appears to be working perfectly. I have Z fighting problems elsewhere if I keep using that Z buffer function, though, so I just set it when drawing the front cap.
By the way, I just finished implementing shadow volumes in C#. Took me a couple days of coding and finding some of the worst bugs I''ve ever had in my life, but it works and I''m incredibly happy now :D
By the way, I just finished implementing shadow volumes in C#. Took me a couple days of coding and finding some of the worst bugs I''ve ever had in my life, but it works and I''m incredibly happy now :D
#14 Members - Reputation: 1102
Posted 16 September 2003 - 09:07 AM
I later modified by shadow algorithm a bit and manually transformed vertices for my caps to world space instead of having DirectX transform them from object space all the way to the screen. I think it introduced precision problems because I started having massive Z fighting problems. I later determined that you can simply set the Z function to Never for the front caps, since you never want them covering your existing geometry anyway.
#17 Members - Reputation: 122
Posted 30 October 2003 - 01:07 PM
I''ve implemented the zfail and it works fine. I have 5 objects in the scene, cube, torus, sphere, teapot, and cone. With the sphere up in front (relative to the eye), I''m seeing the other objects shadow rendered through the sphere, as though the sphere is transperant. I''m assuming I''m suppose to sort my objects, just need confirmation. Should my rendering go like so:
for each sorted object
render the object
render the shadow
thanx,
-Jonathan
for each sorted object
render the object
render the shadow
thanx,
-Jonathan






