SHADOW MATRIX - THEORY AND PRACTICE

The subject of this text is some theoretical and practical explanations of how to produce shadow from spotlight using OpenGL. It is assumed that you know some basic programming, a bit of Linear Algebra and Analytic Geometry and some basics of OpenGL, although this can easily be applied using some other graphic libraries.
INTRODUCTION
If you read "Red Book", you can find there useful article on this topic. But. But what? But it isn't enough. I know because I tried it. Before we jump in deducing, there are few things we must always have on our minds ( this works with OpenGL ):
1. Every vertex ( extended point ) have four "coordinates" : x, y, z, w . First three are coordinates in three-dimensional space, and fourth is used, if I may say, for scaling. That is, every vertex ( x, y, z, w ) is mapped to ( x/w. y/w. z/w ). What if w=0? I really don't know.
2. Transformation matrices are 4x4 ( four rows and four columns ), and they are stored in column - order : if you declare float m[16] that means following matrix :

3. If M is matrix that is already on the matrix stack and you do some transformation which produce new matrix N ( or just multiply it with glMultMatrix() ), the new matrix on the stack will be M*N. And remember that the vertices are mapped in this way : v' = Mv.
THEORY
Here goes the part from "Red Book", with some more details. Assume that the light is in L( 0, 0, 0 ), and the plane on which we want to project shadow is given by ax + by + cz + d = 0, where n( a, b, c ) is vector perpendicular to plane. The one thing that you don't want is that the L is on plane! In this case, it is enough that d<>0. Now, we want to produce matrix, that maps each vertex v to proper vertex v' that lies on the given plane. The coordinates of this vertex is found as intersection of plane and the line that is defined by L and v :

This line is given by ( one can easily check this ) x = k*vx, y = k*vy, z = k*vz, where vx, vy and vz are coord's of vertex v, and k is an arbitrary real number. Plugging this in plane equation gives us :
k*( a*vx + b*vy + c*vz ) + d = 0 ,or in the vector form k*n*v + d = 0.
Second * in last equation is meant to be dot product. Now extract k from this and plug into line to obtain v':
k = -d/(n*v) and v' = -d*v/(n*v).
Notice that n*v shouldn't be 0. This means that v must not be parallel to plane, because then there is no intersection between light line and plane. Finally, our matrix looks like :

To check this, let multiply vertex v( vx, vy, vz, 1 ) by this matrix :

Remember what we sad about fourth coordinate?
Now, suppose that light source isn't in ( 0, 0, 0 ), but in some arbitrary L( Lx, Ly, Lz ).First thought is : lets translate world by vector L and then apply previous matrix. We can go back by inverse translation.
It's good, but not enough. What we have forgot? Plane! If we make matrix from the original plane, and translate world, it's equation remains and that is not good. What we want is the new plane in same relative position to light. Luckily, it isn't hard because the normal vector doesn't change, and we have to recalculate only d. If we translate every point that lies on plane, we have :
a(x + Lx ) + b(y + Ly) + c(z + Lz) + d = 0
which leads to
ax + by + cz + aLx + bLy + cLz + d = 0 ;
so, if we mark fourth parameter of translated plane as d', we have
d' = n*L + d ,
using the vector form. So, if we put this instead of d in previous matrix, we have our shadow matrix!
PRACTICE
Now that we have derived matrix, let see how can we use it in real applications.
In addition to previous, let T is the matrix that translates world to light source L and T^-1 is it's inverse. You might want some matrix that transforms your model - let call it M. Finally, if you're moving through your scene, you probably have some camera matrix which we can call C. Presuming this, order line of matrix multiplications should look like this :
C*T*S*T^-1*M
where S is shadow matrix.
And that is almost all. I tried this approach and put it in my code and guess what happened? Nothing! Whaaaaat?!!?? All this and now you telling me...... OK. Wait a second. I didn't say that calculus was bad. So what went wrong? Something that I really didn't expect, found, once again, in the "Red book". They say : some OpenGL systems might not handle negative fourth coordinate (w) correctly; so keep them positive. I must admit that I was surprised, since I'm mathematician. Doesn't you simply divide first three coords with fourth? But what a heck. I just want my shadow!
And now arised a new problem : how can I know that the vertex, after all transformations, has negative w coordinate in time of making shadow matrix, and before matrices C and M are even applied? And I found intermediate solution. Since my code didn't produce any shadow, I realize that all my w coordinates are negative. So make them positive in S matrix! How? Remember that ( x, y, z, w ) is mapped to ( x/w, y/w, z/w ); if you add negative sign to x, y, z and w, you get same point, with opposite sign of w. If it was negative, now it's positive. Hence, multiply S by -1, and you'll obtain this matrix :

This time it works! Here some mathematical background, which wouldn't be derived here.
What we are interested in is a last row of matrix that will multiply given vertex, because it gives last coordinate of transformed vertex. Regarding the order of matrices in multiplaying sequence T*S*T^-1, it is not too hard to calculate this row ( translation matrix can be found in "Red Book" ); it looks like this :

So, when you multiply this with column vector ( vx, vy, vz, 1 ), fourth coordinate would be
-a*vx - b*vy - c*vz + a*Lx + b*Ly + c*Lz,
or in the vector form :
n*L - n*v, hence n*( L - v ) .
What this telling us? If this dot poduct is negative, it means that the ray, starting at L, and going through v, wouldn't hit the plane! More surprises!
That's all folks! Happy programming, and casting shadows!

download example

back to main page

Hosted by www.Geocities.ws

1