An easy-to-implement type of shadow can be created using projection transforms [96,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:
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); }