The foundations of real-time graphics are
rarely based on principles from physics and optics. In a way, the lighting
equations we’ll cover in this section are cheap hacks, simple models based
on rather shallow empirical observations. We’ll be demonstrating three
different lighting models: ambient lighting (subtle,
monotone light), diffuse lighting (the dull matte
component of reflection), and specular lighting
(the shiny spot on a fresh red apple). Figure 1 shows how these three lighting models
can be combined to produce a high-quality image.
Of course, in the real world, there are no such
things as “diffuse photons” and “specular photons.” Don’t be disheartened
by this pack of lies! Computer graphics is always just a great big hack at
some level, and knowing this will make you stronger. Even the fact that
colors are ultimately represented by a red-green-blue triplet has more to
do with human perception than with optics. The reason we use RGB? It
happens to match the three types of color-sensing cells in the human
retina! A good graphics programmer can think like a politician and use
lies to his advantage.
1. Ho-Hum Ambiance
Realistic ambient lighting, with the soft,
muted shadows that it conjures up, can be very complex to render , but ambient lighting in the context of
OpenGL usually refers to something far more trivial: a solid, uniform
color. Calling this “lighting” is questionable since its intensity is
not impacted by the position of the light source or the orientation of
the surface, but it is often combined with the other lighting models to
produce a brighter surface.
2. Matte Paint with Diffuse Lighting
The most common form of real-time lighting is
diffuse lighting, which varies its brightness
according to the angle between the surface and the light source. Also
known as lambertian reflection, this form of
lighting is predominant because it’s simple to compute, and it
adequately conveys depth to the human eye. Figure 2 shows how diffuse lighting works. In
the diagram, L is the unit length
vector pointing to the light source, and N is the surface normal,
which is a unit-length vector that’s perpendicular to the surface.
The diffuse factor
(known as df in Figure 2) lies between 0 and 1 and gets
multiplied with the light intensity and material color to produce the
final diffuse color, as shown in Diffuse color.
Diffuse color
df is computed by taking
the dot product of the surface normal with the light direction vector
and then clamping the result to a non-negative number, as shown in Diffuse coefficient.
Diffuse coefficient
The dot product is another operation that you
might need a refresher on. When applied to two unit-length vectors
(which is what we’re doing for diffuse lighting), you can think of the
dot product as a way of measuring the angle between the vectors. If the
two vectors are perpendicular to each other, their dot product is zero;
if they point away from each other, their dot product is negative.
Specifically, the dot product of two unit vectors is the cosine of the
angle between them. To see how to compute the dot product, here’s a
snippet from our C++ vector library (see the appendix for a complete
listing):
template <typename T>
struct Vector3 {
// ...
T Dot(const Vector3& v) const
{
return x * v.x + y * v.y + z * v.z;
}
// ...
T x, y, z;
};
Warning:
Don’t confuse the dot product with the
cross product! For one thing, cross products produce vectors, while
dot products produce scalars.
With OpenGL ES 1.1, the math required for
diffuse lighting is done for you behind the scenes; with 2.0, you have
to do the math yourself in a shader. You’ll learn both methods later in
the chapter.
The L vector
in Diffuse coefficient can be computed like
this:
In practice, you can often pretend that the
light is so far away that all vertices are at the origin. The previous
equation then simplifies to the following:
When you apply this optimization, you’re said
to be using an infinite light source. Taking each
vertex position into account is slower but more accurate; this is a
positional light source.
3. Give It a Shine with Specular
Diffuse lighting is not affected by the
position of the camera; the diffuse brightness of a fixed point stays
the same, no matter which direction you observe it from. This is in
contrast to specular lighting, which moves the
area of brightness according to your eye position, as shown in Figure 3. Specular lighting mimics the shiny
highlight seen on polished surfaces. Hold a shiny apple in front of you,
and shift your head to the left and right; you’ll see that the apple’s
shiny spot moves with you. Specular is more costly to compute than
diffuse because it uses exponentiation to compute falloff. You choose
the exponent according to how you want the material to look; the higher
the exponent, the shinier the model.
The H vector
in Figure 3 is called the
half-angle because it divides the angle between the
light and the camera in half. Much like diffuse lighting, the goal is to
compute an intensity coefficient (in this case, sf)
between 0 and 1. Specular lighting shows how to
compute sf.
Specular lighting
In practice, you can often pretend that the
viewer is infinitely far from the vertex, in which case the E vector is substituted with (0, 0, 1). This
technique is called infinite viewer. When E is used, this is called local
viewer.