# 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.

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

• If the normal points toward the light source, the polygon will receive the full light value and will be drawn in the brightest color. Mathematically, the cosine of zero degrees is 1.0, and thus the polygon will receive 100% of the light.

• If the normal of the polygon is at some angle greater than 0 but less than 90 degrees, the polygon will receive less light and thus will be drawn a darker shade. Some example angles are:
cos(30) = 0.866 or 87% of the light
cos(45) = 0.707 or 71% of the light
cos(60) = 0.500 or 50% of the light
cos(75) = 0.259 or 26% of the light

• If the polygon normal vector and the light source are at 90 degrees, the polygon will be in shadow since the cosine of 90 degrees is zero (0).

• Angles between 90 degrees and 270 degrees will return negative values. Thus, the dot product for vectors in this range will be negative and the surface will be colored dark as being in the shadow.

• As the angle exceeds 270 degrees and approaches 360, the cosine will again be positive and the light will once again be hitting the polygon.

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.