Simple Lighting

The goal of this section is to add some realistic shading to the polygons. In the previous chapter, the last demo was a solid-colored 3D house. The sides of the house were a shade darker than the front, so basically we faked the polygon shading. Here, we do real shading with light sources instead of coloring the polygons by hand.

Diffuse Reflection

The form of lighting you'll use here is diffuse reflection using Lambert's law. This law basically says that the intensity of reflected light depends on the angle between the normal of the surface and the direction of the light source, as shown in .

Diffuse reflection lights a polygon based on the angle between the polygon's normal and the direction of the light source.
Diffuse reflection lights a polygon based on the angle between the polygon's normal and the direction of the light source.

The smaller the angle is, the more light is reflected. Likewise, larger angles reflect smaller amounts of light. Assuming the normal and direction of the light source are normalized, this can be expressed mathematically using the following equation, where i is the intensity of the reflection.

i = N•L

An easy way to see diffuse reflection work in the real world is to look at a room with one light source: For example, a dark room in which the walls and ceiling are the same color and a light is emitted from a light source near the ceiling. If you look at a corner between a wall and a ceiling, the ceiling near the corner will be darker than the wall. This is because the wall receives light directly from the light source, while the ceiling receives light from a larger angle. Thus, the ceiling appears a darker shade.

Ambient Lighting

Also, during the day in a typical room, you might not need any lights on because the sunlight comes in a window and bounces around the room, lighting everything in the room. Modeling light that bounces around the room is a bit difficult to calculate, so instead you can approximate this effect with ambient lighting. Ambient light limits the minimum light intensity an object can have. For example, in a sunlit room you might want everything in the room to have a minimum light intensity of 0.7. You can express this easily by giving a polygon an ambient light intensity:

i = a+N•L

The ambient light intensity should be between 0 and 1.

Light Source Intensity

Just like a polygon can have an ambient light intensity, the light source itself can have an intensity value. This is like the difference between a 60W and 100W light bulb. Using l to describe the light source intensity, the equation becomes this:

i = a+l(N•L)

Distance Falloff

Taking the light source intensity a step further, you can make the intensity of the light diminish with distance. That is, the farther away from a light source an object is, the dimmer the light source appears. The falloff distance is the distance that a light source is no longer visible. Using l as the light source intensity, f as the falloff distance, and d as the distance from the object to the light source, the equation becomes this:

l' = l(f-d)/(f+d)
i = a+l'(N•L)

Implementing a Point Light

We express a light source with an intensity and optional falloff distance in the PointLight3D class in .

Listing 8.6 PointLight3D.java

package com.brackeen.javagamebook.math3D;
/**
 A PointLight3D is a point light that has an intensity
 (between 0 and 1) and optionally a distance falloff value,
 which causes the light to diminish with distance.
*/
public class PointLight3D extends Vector3D {
 public static final float NO_DISTANCE_FALLOFF = -1;
 public float intensity;
 public float distanceFalloff;
 ...
 /**
 Gets the intensity of this light from the specified
 distance.
 */
 public float getIntensity(float distance) {
 if (distanceFalloff == NO_DISTANCE_FALLOFF) {
 return intensity;
 }
 else if (distance >= distanceFalloff) {
 return 0;
 }
 else {
 return intensity * (distanceFalloff - distance)
 / (distanceFalloff + distance);
 }
 }
}

The PointLight3D class is a Vector3D that contains an intensity and distance falloff value for the light. Its getIntensity() method gets the light intensity for a particular distance.