The general idea is that we'll render from the light's point of view, save that to a texture, and then on each pixel, determine whether the pixel is in shadow by comparing the texture's depth value with a calculated depth value.
There are two common methods for doing this. One is to render into the window and copy that result to a texture. The other is to render directly into the texture using the Framebuffer Objects extension.
glCopyTexImage
Render the scene into a window, and call glCopyTexImage with internal format set to GL_DEPTH_COMPONENT to copy the depth buffer to the texture.
The rest of the details can be found in the man page.
This function does the same thing as glTexImage2D, so apart from this you just have to generate the texture and set its filtering correctly during initialization.
Important notes:
glViewport and then set it back to the window size after you're done rendering.
Note: the graphics cards in WeH 5336 do not support depth-texture framebuffer objects, so you'll have to stick with using glCopyTexImage2D
If you want to use FBOs to use do render-to-texture, the specification for the extension is available here. Just kidding, you don't have to read that. Since there aren't any good resources available online for this stuff, here is some sample code to set up a render-to-depth-texture loop.
Once this has been successfully completed, the contents of the depth buffer will look something like the following image.
Note that you should be pretty careful about how you select your z-near and z-far clipping planes. In this case, I've set z-near to be justin front of the guy's head and z-far to be just beyond the floor, which efficiently uses the full range of the z-buffer. While it isn't necessary to be this excessively precise, you should keep an eye on your range to make sure that you aren't wasting depth, since that will result in artifacts.
To convert between world coordinates and texture coordinates, you need to do two steps.
In practice, probably the best way to do this is, each frame,
gluPerspective and gluLookAt)Mat4s lightModelView/ProjectionMatrix by calling glGetDoublev (GL_MODELVIEW_MATRIX, lightModelViewMatrix), and likewise for the projection matrixglPolygonOffset (0, 1) and enabling GL_POLYGON_OFFSET_FILL
glActiveTexture you're using) by calling glMatrixMode (GL_TEXTURE) and clear the matrix with glLoadIdentity ()glTranslatef (0.5, 0.5, 0.5)glScalef (0.5, 0.5, 0.5)glMultMatrixd (lightProjectionMatrix)glMultMatrixd (lightModelViewMatrix)glGetDoublev just like you got the light's modeview matrix, invert it using the Mat4 invert function, then call glMultMatrixd)GL_MODELVIEW matrix, appropriate textures, etc).gl_TextureMatrix[i]*gl_ModelViewMatrix and save that in a varying variable. This will convert the points into the shadow map's coordinate system.
Note that just as before, multiplying by gl_ModelView/NormalMatrix will transform things into eye coordinates, and the lights will be in eye coordinates for lighting calculations.
If then, in your fragment program, you have the resulting varying texture coordinate as shadowTexCoord and the shadow texture map as a sampler2D called shadowMap then reading from the texture using texture2DProj (shadowMap, shadowTexcoord), the result will be the following image.
That is, the color that you read out of the texture will be exactly the depth of the nearest occluder of that pixel.
To calculate each pixel's distance from the camera, you can just look at the value shadowTexCoord.z/shadowTexCoord.w
If you render that, you'll end up with an image that looks like
And now, all you need to do is compare those two depth values to determine what is in shadow and what's not.
GLSL makes this a bit easier for us.
We can specify that the depth texture will be used for comparing depth values by setting the following texture parameters using glTexParameteri when the shadow texture is bound.
GL_TEXTURE_COMPARE_MODE to GL_COMPARE_R_TO_TEXTURE_ARB will specify that we want to compare texture values to texture R coordinates (and the R will be set up to hold the depth value by the function we'll call).GL_TEXTURE_COMPARE_FUNC to GL_LEQUAL will specify that the comparison function should return 1 when the texture coordinate is less or equal than the texture value (i.e. when the object is not in shadow).
To tell GLSL that we're using texture comparison instead of texture mapping, define the shadow map texture as uniform sampler2DShadow shadowMap instead of uniform sampler2D shadowMap.
Finally, with all of this set up, calling the function shadow2DProj (shadowMap, shadowTexCoord) will return a vec4 that is 0 if the object is in shadow in 1 if it is not in shadow.
If you then combine these results with your normal mapping, you can create an image like this, and give John Carmack a run for his money (or something).
One thing that I mentioned earlier was this z-fighting thing.
Calling glPolygonOffset (2, 2) pushes the scene back by a small epsilon in the z-buffer before rendering it.
This corresponds to allowing a little bit of wiggle room for the depth values, so that if they're a little bit off, we err on the side of assuming that they're not in shadow. If you didn't use glPolygonOffset then you'll get a kind of gnarly looking image caused by z-fighting as seen below.