next up previous contents
Next: Projective Shadow Trade-offs Up: 11.3 Creating Shadows Previous: 11.3 Creating Shadows   Contents

11.3.1 Projective Shadows

An easy-to-implement type of shadow can be created using projection transforms [99,10]. An object is simply projected onto a plane, then rendered as a separate primitive. Computing the shadow involves applying a orthographic or perspective projection matrix to the modelview transform, then rendering the projected object in the desired shadow color.

Here is the sequence needed to render an object that has a shadow cast from a directional light on the $z$ axis down onto the $x$, $y$ plane:

  1. Render the scene, including the shadowing object in the usual way.
  2. Set the modelview matrix to identity, then call glScalef1.f, 0.f, 1.f(1.f, 0.f, 1.f).
  3. Make the rest of the transformation calls necessary to position and orient the shadowing object.
  4. Set the OpenGL state necessary to create the correct shadow color.
  5. Render the shadowing object.

In the last step, the second time the object is rendered, the transform flattens it into the object's shadow. This simple example can be expanded by applying additional transforms before the glScalef() call to position the shadow onto the appropriate flat object. Applying this shadow is similar to decaling a polygon with another coplanar one. Depth buffering aliasing must be taken into account. To avoid depth aliasing problems, the shadow can be slightly offset from the base polygon using polygon offset, the depth test can be disabled, or the stencil buffer can be used to ensure correct shadow decaling. The best approach is probably depth buffering with polygon offset. This way the depth buffering will minimize the amount of clipping you will have to do to the shadow.

The direction of the light source can be altered by applying a shear transform after the glScalef() call. This technique is not limited to directional light sources. A point source can be represented by adding a perspective transform to the sequence.

Although you can construct an arbitrary shadow from a sequence of transforms, it might be easier to just construct a projection matrix directly. The function below takes an arbitrary plane, defined as a plane equation in $Ax + By + Cz + D = 0$ form, and a light position in homogeneous coordinates. If the light is directional, the $w$ value should be 0. The function concatenates the shadow matrix with the current matrix.

static void
myShadowMatrix(float ground[4], float light[4])
    float  dot;
    float  shadowMat[4][4];

    dot = ground[0] * light[0] +
          ground[1] * light[1] +
          ground[2] * light[2] +
          ground[3] * light[3];
    shadowMat[0][0] = dot - light[0] * ground[0];
    shadowMat[1][0] = 0.0 - light[0] * ground[1];
    shadowMat[2][0] = 0.0 - light[0] * ground[2];
    shadowMat[3][0] = 0.0 - light[0] * ground[3];
    shadowMat[0][1] = 0.0 - light[1] * ground[0];
    shadowMat[1][1] = dot - light[1] * ground[1];
    shadowMat[2][1] = 0.0 - light[1] * ground[2];
    shadowMat[3][1] = 0.0 - light[1] * ground[3];
    shadowMat[0][2] = 0.0 - light[2] * ground[0];
    shadowMat[1][2] = 0.0 - light[2] * ground[1];
    shadowMat[2][2] = dot - light[2] * ground[2];
    shadowMat[3][2] = 0.0 - light[2] * ground[3];
    shadowMat[0][3] = 0.0 - light[3] * ground[0];
    shadowMat[1][3] = 0.0 - light[3] * ground[1];
    shadowMat[2][3] = 0.0 - light[3] * ground[2];
    shadowMat[3][3] = dot - light[3] * ground[3];

    glMultMatrixf((const GLfloat*)shadowMat);

next up previous contents
Next: Projective Shadow Trade-offs Up: 11.3 Creating Shadows Previous: 11.3 Creating Shadows   Contents