Shadow Volumes and MD2

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

Really, the model format doesn’t matter much. After loading the model, the algorithm for shadow volume generation is always the same:

Preprocess:

[ul][li] Compute a plane equation for every face (triangle) of the model[] Find all edges and store adjacent faces[/ul][/li]Shadow volume:
[ul][
] 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[/ul]

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

Shadow volume:
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!

Originally posted by Nick’s a Novice:
[b]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
[/b]
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.

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

Shadow volume:
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
[/b]
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).

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!

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 :wink:

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!

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
	ViewAngletoRadians();								// modifications happen in degrees displays are in radians
	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_EDGES);		                // draw extruded shadow edges
			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_EDGES);		                // draw extruded shadow edges
			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

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.

I haven’t played with this in a while, and I used some extensions.

For each light:

Draw shadow volumes:
// Clear
glClearStencil(128);
glStencilMask(~0);
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);
glDepthMask(0);
glEnable(GL_DEPTH_CLAMP_NV);
// Color.
glColorMask(0,0,0,0);
// Draw shadow voulmes…

Then add diffuse light:
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);
glDepthMask(1);
glDepthFunc(GL_LESS);
glEnable(GL_DEPTH_TEST);
// Add Color.
glBlendFunc(GL_ONE,GL_ONE);
glEnable(GL_BLEND);
glColorMask(1,1,1,1);
// Draw …

This worked great, but you may need to fiddle with
glPolygonOffset to eliminate self occlusion, or try extruding back-faces instead of front for closed models.

Sorry about the use of extensions, but I haven’t fooled with this in some time, and I just ripped this out of some old code.

There should be some complete demos out there somewhere. I think NVIDIA has a demo of this technique with MD3 models.

Oh yeah, here it is:
http://download.developer.nvidia.com/dev…dow_volumes.zip