GL_ARB_shadow
getShadowMap()
Now it starts to get interesting. To get the shadow map, we need to turn off drawing to the color buffer (it’s a waste of time and we would need to clear it afterwards if we drew to it). We then set up the light source viewpoint using gluLookAt. If you really want to get into it, you would also set up a light specific projection matrix, but GLWindow’s default matrix is good enough for this example. We also set up the polygon offset. To get a better understanding of what this does, I suggest looking it up in the OpenGL 2.0 specs. Basically, it alters the depth value stored in the depth buffer while rendering and helps us avoid problems during the comparison step of shadow mapping. Now that we are all set up, we draw the shadow casters in the scene, copy the depth buffer and set everything back that we changed (color mask, depth buffer, and modelview matrix).
// draws the shadow casters from the light view's
// location and saves depth values to a texture
void getShadowMap()
{
// turn off drawing to the color buffer
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
gluLookAt(
0.0, 0.0, 5.0,
0.0, 0.0, 0.0,
0.0, 1.0, 0.0);
glPolygonOffset(10.0, 4.0);
glEnable(GL_POLYGON_OFFSET_FILL);
drawShadowCasters(false);
glDisable(GL_POLYGON_OFFSET_FILL);
glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, 0, 0, SIZE, SIZE, 0);
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
// clear the depth buffer and current matrix
glClear(GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
}
setupMatrices()
Now we set up all those matrices that we talked about earlier. The bias matrix is created first and is well known. The light’s modelview matrix is taken from the same gluLookAt code used earlier and the light’s projection matrix is read from the current GL_PROJECTION_MATRIX. These three matrices are then multiplied together into the GL_TEXTURE_MATRIX. An identity matrix is then created and loaded into the code that will generate texture coordinates for use. This is the part that gives us the inverse of the camera’s modelview matrix. Look it up in the OpenGL 2.0 specs for more information.
void setupMatrices()
{
// matrices
double proj[16];
double light[16];
double bias[16] =
{
0.5, 0.0, 0.0, 0.0,
0.0, 0.5, 0.0, 0.0,
0.0, 0.0, 0.5, 0.0,
0.5, 0.5, 0.5, 1.0
};
double texMatrix[16];
glMatrixMode(GL_TEXTURE);
glLoadIdentity();
glGetDoublev(GL_PROJECTION_MATRIX, proj);
glPushMatrix();
{
gluLookAt(
0.0, 0.0, 5.0,
0.0, 0.0, 0.0,
0.0, 1.0, 0.0);
glGetDoublev(GL_TEXTURE_MATRIX, light);
}
glPopMatrix();
glLoadIdentity();
glMultMatrixd(bias);
glMultMatrixd(proj);
glMultMatrixd(light);
memset(texMatrix, 0, 16*sizeof(double));
for(int i = 0; i < 4; i++) texMatrix[i*4 + i] = 1.0;
glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
glTexGeni(GL_R, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
glTexGeni(GL_Q, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
glTexGendv(GL_S, GL_EYE_PLANE, &texMatrix[0]);
glTexGendv(GL_T, GL_EYE_PLANE, &texMatrix[4]);
glTexGendv(GL_R, GL_EYE_PLANE, &texMatrix[8]);
glTexGendv(GL_Q, GL_EYE_PLANE, &texMatrix[12]);
glMatrixMode(GL_MODELVIEW);
}
draw()
And finally, we reach the draw method. It was shown earlier, but I'll just metion how it pulls everything together. We start it as we start any draw method: by clearing the depth and color buffers and loading the identity matrix. We then get the shadow map and set up the camera. The matrices are set up after the call to gluLookAt; this is important because it is how we get the inverse of the modelview matrix. Then we draw the scene.
void draw()
{
// clear buffers and load indentity matrix
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
getShadowMap();
gluLookAt(
-5.0, 3.0, 5.0,
0.0, 0.0, -1.0,
0.0, 0.0, 1.0);
setupMatrices();
drawShadowCasters(true);
drawShadowRecievers();
}