Simple Shadow Casting

Simple Shadow Casting


Take a look at the picture on the left. It depicts two spheres sitting on, or floating above a green base. It's hard to tell whether they are touching the base or if they are floating above it.
Now look at the picture on the left. Suddenly it has all become clear. The red sphere is the larger of the two, and is resting at the back of the green base. The blue one is floating above the base, and is nearer. The only thing that is different between the two is that the picture on the right has shadows, otherwise they are identical.
As you can see, shadows provide the viewer with a great deal of information about a scene. An object with a shadow has more presence than one without. Shadows give depth to a scene and reinforce our perception of the location of lightsources. Take a look at Quake. It's shadows add mood and atmosphere. Imagine how boring it would look with everything at the same brightness.


A Simple Algorithm For Casting Shadows

There are a great many algorithms for casting shadows in a 3D scene. As is the case with all algorithms, some are faster than others, some are better. As usual, you have to choose the right one for the job. I am going to present a very simple algorithm here. It is only suitable for casting sharp shadows from polygonal objects onto a single, infinite plane. For example, you might use it on a simple flight sim where the land is totally flat. It is very restricted, but it is fast and simple, and will give you some insight into how shadows work.

For each polygon in a scene, another polygon is created to act as it's shadow. The vertices of the shadow polygon will all lie on the plane. The algorithm goes like this.

For each vertex:

  • Construct a vector from the light source to the vertex.
  • Lengthen that vector so that it touches the plane.
  • Add that vector to the position of the lightsource, and you have the position for this vertex of the shadow polygon.

    Let's take an example. Look at the picture above. The polygon A,B,C is casting a shadow from the lightsource L onto the plane to create polyon Sa,Sb,Sc. The plane in this case is totally horizontal, i.e. parallel to the X and Z axes. I'll start with vertex A and cast it's shadow Sa.

  • Construct a vector from the light source to the vertex.
    V = A - L

  • Lengthen that vector so that it touches the plane.
    V = V * ((y1+y2) / y1)

    y1+y2 is the distance from the lightsource to the plane. y2 is the distance from the point A to the plane. Because this plane is horizontal, and passes throught the origin, the values of y1 and y2 are easy to calculate. y2 is the Y coordinate of the vertex A, and (y1+y2) is the Y coordinate of the point L.

  • Add that vector to the position of the lightsource, and you have the position for this vertex of the shadow polygon.
    Sa = L + V

    Do the same for the other two vertices B and C to create Sb and Sc. Now you can draw the polygon Sa, Sb, Sc as a shadow.


    Casting A Shadow Onto An Arbitary Plane

    This follows exactly the same procedure as above, but now it becomes slightly more tricky to calculate the values of y1 and y2. You will first have to define your plane. There are a couple of ways to do this, but I shall show you the method where you define the plane by a point on the plane and a vector perpendicular to it.

    Suppose the normal to the plane is (Na, Nb, Nc)
    and the point whose distance from the plane you want to determine is (Px, Py, Pz)

    		   (Na*Px + Nb*Py + Nc*Pz) 
    	Distance = -----------------------
    		    sqrt(Na2 + Nb2 + Nc2)
    
    This is assuming that the plane passes through the origin.


    Important Notes

    There are a couple of traps here. This method will attempt to draw a shadow in all circumstances. It will draw a shadow if the light is between the plane and the polygon, and if the light is on the other side of the plane.
    To prevent this from happening, either detect it's occurence, or prevent it altogether. If you are always going to have a horizontal plane, then just make sure the light source stays above all the polygons. If you want to have a tilting plane, or if you can't guarantee the positions of any objects or light sources, then you will have to detect. I don't really have the time to go into details here as it is way past my bed time. If anyone really wants to do the detection, and can't figure it out, drop me a line and I'll see what I can come up with.