PDA

View Full Version : Shadow Volumes and MD2

11-13-2005, 07:08 PM
Hey this may be a totally over asked question, but I have been stuck on this for a while. I, like everyone else and their brother, am writing my own game engine and it looks really great so far, but I am stuck on the shadows. I think stenciled shadow volumes are the way to go. I have a very large height field terrain map that the shadows will be cast on and right now I am using md2's. I will eventually switch to md5's I think, but for now I just want to get basic shadow casting down from the md2's. I have read several shadow volume tutorials, but they are all either using G3D or casting for simple cube objects. If anyone knows of any simple shadow volume tutorials using md2 I would appreciate it. If not if anyone has the time to write a verrrrrry basic tutorial using md2's I would be very appreciative. Thanks in advance! Let me know thanks folks -nharris_coder@hotmail.com

memfr0b
11-14-2005, 06:09 AM
Really, the model format doesn't matter much. After loading the model, the algorithm for shadow volume generation is always the same:

Preprocess:

Compute a plane equation for every face (triangle) of the model Find all edges and store adjacent faces
Compute lit faces using the plane equations and the model space light position Compute silhouette edges (edges where one adjacent face is lit, and the other is not) Extrude silhouette edges Add front and/or back cap

Nick's a Novice
11-14-2005, 07:47 AM
As the name implies I truely am a novice, so let me see if I understand this here...

Preprocess:
1.) Compute normal for every triangle in the model
2.) Store a List of all edges and the 2 faces to which those edges connect

1.) Compute which faces are facing the light using model space light vector and normal vectors from model
2.) find edges where one adjacent face is lit, and the other is not
3.) for all edges found draw a quad using the edge and a corresponding edge that moves along model space light vector at "infinity" ( do I have to draw a quad or just calculate the silhouette at "infinity" )
4.) draw a polygon from these edges at "infinity" to create back cap

Is this correct? I appreciate the help thanks a lot!

memfr0b
11-14-2005, 08:27 AM
Originally posted by Nick's a Novice:
As the name implies I truely am a novice, so let me see if I understand this here...

Preprocess:
1.) Compute normal for every triangle in the model
You should compute a plane equation, not only the face normal. The plane equation has the form ax+by+cz+d=0 where (x,y,z) represents any point on the plane, and (a,b,c,d) are the plane equation coefficients you need to compute.

(a,b,c) is simply a normal on the plane, d is the reciprocal distance of the plane to the coordinate origin in units of the normal. Assuming your triangle is defined by the corner vertices v1, v2, and v3, the following pseudo code snippet derives the plane equation.

2.) Store a List of all edges and the 2 faces to which those edges connect

1.) Compute which faces are facing the light using model space light vector and normal vectors from model
2.) find edges where one adjacent face is lit, and the other is not
3.) for all edges found draw a quad using the edge and a corresponding edge that moves along model space light vector at "infinity" ( do I have to draw a quad or just calculate the silhouette at "infinity" )
4.)draw a polygon at this "infinity" to create back cap
Step 3 and 4 depend a bit on which stencil algorithm you are using. For Carmack's zfail approach, you have to add a dark cap (to close the extruded silhouette of the shadow volume). You can extrude the unlit faces to get this cap, for example. But you have to make sure, the dark cap is not clipped by the far plane of the view frustum. You either have to limit your extrusion depth accordingly, or use an infinite view frustum (with a modified perspective projection matrix).

Nick's a Novice
11-16-2005, 05:34 AM
Ok that makes sense; I'll give that a try this weekend. I do have another question, for the opengl side, I know there are tons of ways of doing this, but does this sound about right?:

1.)Draw the Entire Scene Light
2.)turn off depth and color buffers turn on stencil buffer
3.)switch to front face culling and the stencil buffer to increment
4.)draw models with extruded edges and caps
5.)switch to back face culling and the stencil buffer to decrement (I know this can be done more efficiently with opengl extensions... another thing to be added in later)
6.)draw models with extruded edges and caps
7.)turn on depth and color buffers, and set depth to GL_EQUAL
8.)switch light to darker "shadowed" ambient lighting
9.)redraw the models and the objects I want to cast shadows from the models onto

Does that sound like it will work let me know what you think? I appreciate all the help!

Overmind
11-16-2005, 05:52 AM
The usual algorithm is the following:

1) Draw the Entire Scene with Ambient only (lighting turned on, lightsources turned off)

Then for each light source:
2) - 7) like you described
8) enable lighting and the current light source, but disable ambient (set global ambient to black)
9) set stencil test to "draw outside shadow" and blending to ONE, ONE
10) redraw the scene

Either this, or just a simple fullscreen quad with an appropriate blend mode to darken shadowed areas instead of steps 8) - 10). That's the cheapest method, but obviously not as realistic ;)

Nick's a Novice
11-16-2005, 07:26 AM
Thanks, thats great! Thanks all for your help I'll give all this a try this weekend. Thanks so much for the quick answers and all the help folks!

Nick's a Novice
11-19-2005, 02:29 PM
hey for some reason I can't get this to work right, I got the shadow edges and caps methods to work right seemingly... but i still see no shadows... any thoughts?

void RenderScene()
{

glLightModelfv(GL_LIGHT_MODEL_AMBIENT, ambientLightModelLandandSky); // draw scene with light ambient model
IncrementTicker(); // time based animation and frame rate goodies
glInitNames(); // This clears the name stack so we always start with 0 names.
HandleKeys(); // handle keyboard input
SetupCamera(); // setup the cameras position and look position
glFlush(); // flush out all gl commands

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); // clear depth color BUFFERS
glLoadIdentity(); // reset the identity matrix
SwitchViewMode(); // set the look at based on view mode
glDepthFunc(GL_LEQUAL); // set depth less than or equal

glCullFace(GL_BACK); // back face culling (unscene polys aren't drawn)

DrawSkyBox(); // draw the skybox
DrawSky(); // draw the clouds and other sky ellements

glEnable(GL_LIGHTING); // enable ambitent lighting

DrawLand(); // call stored list to draw land
glColor4f(0.0f, 0.2f, 0.3f, 0.65f);
PoseObjects(); // pose objects
DrawObjects(); // draw objects
glDisable(GL_LIGHTING); // disable lighting
DrawWater(); // draw water blended clear

// store current OpenGL state
glPushAttrib(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_POLYGON_BIT | GL_STENCIL_BUFFER_BIT);
glColorMask(0, 0, 0, 0); // do not write to the color buffer
glDepthMask(0); // do not write to the depth (Z) buffer

glEnable(GL_CULL_FACE); // cull faces (back or front)
glEnable(GL_STENCIL_TEST); // enable stencil testing

glStencilFunc(GL_ALWAYS, 0, ~0); // set the reference stencil value to 0

glStencilOp(GL_KEEP, GL_INCR, GL_KEEP); // increment the stencil value on Z fail

glCullFace(GL_FRONT); // draw only the back faces of the shadow volume

DrawObjectsNA(SHADOW_CAPS); // draw front and back caps

glStencilOp(GL_KEEP, GL_DECR, GL_KEEP); // decrement the stencil value on Z fail
glCullFace(GL_BACK); // draw only the front faces of the shadow volume

DrawObjectsNA(SHADOW_CAPS); // draw front and back caps

// restore OpenGL state
glPopAttrib();

// re-draw the model with the light enabled only where
// it has previously been drawn
glDepthFunc(GL_EQUAL);

// update the color only where the stencil value is 0
glEnable(GL_STENCIL_TEST);
glStencilFunc(GL_EQUAL, 0, ~0);
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
glLightModelfv(GL_LIGHT_MODEL_AMBIENT, ambientDarkModel);
glBlendFunc(GL_ONE, GL_ONE);
glEnable(GL_BLEND);
glEnable(GL_LIGHTING);
glColor4f(0.0f, 0.2f, 0.3f, 0.65f);
DrawLand();
glColor4f(0.0f, 0.2f, 0.3f, 0.65f);
DrawObjectsNA(FULL_MODEL);
DrawWater();

glDisable(GL_BLEND);
glDepthFunc(GL_LESS);
glDisable(GL_LIGHTING);
DrawInterface();
WriteFPS();
glFlush(); // flush out gl commands
BufferSwap();

}

Here is my opengl code for displaying it is this all right? if so then my problem has to be elsewhere but i thought this might be a good first place to start looking. Thanks again folks

Nick's a Novice
11-19-2005, 09:25 PM
the whole thing seems to be working individually so i'm thinking the problem has to be in this code here somewhere, because i dont see anything drawn shadowed at the end.

11-20-2005, 09:36 AM
I haven't played with this in a while, and I used some extensions.

For each light:

// Clear
glClearStencil(128);
glClear(GL_STENCIL_BUFFER_BIT);
glStencilFunc(GL_ALWAYS, 128, ~0);
glEnable(GL_STENCIL_TEST);
// Two sided setup.
glEnable(GL_STENCIL_TEST_TWO_SIDE_EXT);
glActiveStencilFaceEXT(GL_BACK);
glStencilOp(GL_KEEP, GL_INCR, GL_KEEP);
glActiveStencilFaceEXT(GL_FRONT);
glStencilOp(GL_KEEP, GL_DECR, GL_KEEP);
glDisable(GL_CULL_FACE);
// Depth.
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LESS);
glEnable(GL_DEPTH_CLAMP_NV);
// Color.

glDisable(GL_STENCIL_TEST_TWO_SIDE_EXT);
glEnable(GL_CULL_FACE);
glCullFace(GL_FRONT);
// Stencil set == 128.
glEnable(GL_STENCIL_TEST);
glStencilFunc(GL_EQUAL, 128, ~0);
glStencilOp(GL_KEEP, GL_KEEP, GL_INCR);
// Depth.
glDisable(GL_DEPTH_CLAMP_NV);
glDepthFunc(GL_LESS);
glEnable(GL_DEPTH_TEST);
glBlendFunc(GL_ONE,GL_ONE);
glEnable(GL_BLEND);