Supercomputer Applications
More on 3D Lighting Using Surface Normals

Normal Vectors

In order for the OpenGL rendering engine to figure out how much light should be hitting a specific polygon region, it is necessary to calculate the normal vector to the surface of that polygon. This will be a directional vector of unit size one (1) that is perpendicular to the surface of the polygon. This is illustrated by the blue triangle and the green surface normal shown in the diagram to the right.

Normal for Triangle An Example
Given a triangular polygon with three vertices in the form ( xn, yn, zn ), it is possible to calculate the normal to that triangle by first describing two directional vectors in the same plane. For instance, given three points:

( Shown in BLUE )
p1 = (x1, y1, z1)
p2 = (x2, y2, z2)
p3 = (x3, y3, z3)

Two possible directional vectors representing the plane of that surface are:
d1 = ( x2 - x1,   y2 - y1,   z2 - z1 )
d2 = ( x3 - x2,   y3 - y2,   z3 - z2 )

In order to find a line perpendicular to those two vectors, it is necessary to find the cross product of these two vectors. Each component of the cross product vector is described below.

cross[x] = d1[y] * d2[z]   -   d1[z] * d2[y]
cross[y] = d1[z] * d2[x]   -   d1[x] * d2[z]
cross[z] = d1[x] * d2[y]   -   d1[y] * d2[x]

Cross product vectors can vary in size depending upon the components, so in order for OpenGL to make some comparisons needed for lighing, it is necessary to "normalize" the result by making them all uniform in some way. This is done by creating a normal vector with distance of one (1) unit long. To scale the normal, it is necessary to divide each component part by the total length or distance of the cross product vector which will scale each portion appropriately.

The length of the cross product vector or its distance is calculated with the following formula:
dist = SQRT( cross[x]2 + cross[y]2 + cross[z]2)

The components of the normal would then become:
norm[x] = cross[x] / dist
norm[y] = cross[y] / dist
norm[z] = cross[z] / dist

The normal is drawn in GREEN in the diagram above.

How OpenGL Uses Normal Vectors for Polygon Lighting

When OpenGL is deciding how much light should be applied to a surface, it takes the direction of the light source and compares it to the normal of the surface of the polygon. If the light strikes the surface from directly above, the object will be colored with the brightest light, whereas if the light strikes the surface at an angle, the surface will be less strongly lit. If the light hits the surface at a 90 degree angle or if the normal is pointing away from the light source, the polygon will receive no light.

Using the Dot Product for Light Calculations

The comparison described above is actually achieved by using a mathematical process called the dot product between two vectors, one that represents the normal for the surface of the polygon and the other that represents the direction of the light source. Mathematically, the dot product is defined by the following formula:

u   .   v   = |u| |v| cos(theta)
where |u| and |v| are the magnitudes of the two vectors and "theta" is the angle between them.
Since the magnitudes of the normal vectors are each of length one (1), the magnitudes can be ignored and therefore dot product merely returns the cosine of the angle between the light source and the normal vector that represents the perpendicular to the polygon region. OpenGL uses this value as a percentage of the amount of light that strikes the polygon region and thus can be used to emulate the effects of light and shadow.

Some Example Lighting Scenarios

Since normal vectors are directional, it is necessary to do all calculations in the same manner. For instance, if a triangle's vertices are given to the formula in a counter clockwise fashion rather than clockwise, the resulting normal vector will point in the opposite direction. This can cause problems if calculations are not done in a consistent manner between adjacent triangles. Some very unrealistic effects happen if a surface generated by polygon regions has some of the triangles brightly lit while others nearby are in thought to be in the shadow because their normals are pointing in opposite direction!

There are a number of other techniques for averaging light values for adjacent polygons, but that will depend upon the characteristics of the object being modeled, such as does the model have sharp edges or are the polygons attempting to model a smooth curved ssurface. Readers are encouraged to investigate advanced lighting concepts on their own.