i
i
i
i
i
i
i
i
276 7. Advanced Shading
rection vector l and irradiance contribution (measured in a plane perpen-
dicular to the light source) E
L
. Many different types of light sources can
be defined, leading to different ways to compute l and E
L
—see Section 7.4.
In the material phase, the BRDF parameters and surface normal (as well
as the tangent and bitangent, in the case of anisotropic materials) are
found via interpolation or texturing. These values are used to evaluate the
BRDF for l and the view vector v. The result of the BRDF evaluation is
multiplied by a cosine factor and by E
L
to produce the outgoing radiance
contributed by the light source. Both phases are repeated for each light
source and the results are summed to produce the total outgoing radiance
for the fragment.
In many real-time applications, numerous light sources of various types
can be applied to surfaces described by a variety of materials. The sheer
number of combinations can pose difficulties. Imagine a game that sup-
ports just three types of light source (point, directional and spot). Each
mesh can be affected by no more than six light sources at once, and can
have one of five material types. In this relatively simple case, there are 420
shader combinations.
21
The number of combinations grows rapidly with
every addition—just adding a fourth light type (e.g., a projected texture
light) increases the number of combinations to 1050.
22
In real applica-
tions, the number is even higher. Valve’s Half-Life 2 has 1920 pixel shader
combinations [848].
In addition, the fact that both the light and material phase need to
be repeated for each light source affecting the object can lead to great
inefficiencies. Imagine a mesh modeling a long hallway, which is lit by
twenty spaced light sources. No location is lit by more than two light
sources, but if dynamic branches are not employed, then all twenty light
sources need to be computed for every pixel.
The most straightforward solution to both problems is to loop over light
sources dynamically in the pixel shader. Unfortunately, dynamic branches
in the pixel shader perform poorly on many GPUs. Even if branches are
reasonably fast, they still introduce some amount of overhead, which may
be unpalatable for performance-sensitive applications.
If dynamic branches are not used, then a different shader needs to
be generated for each possible combination of lights and material type.
Obviously, authoring each combination manually is impractical. A common
solution to this problem is to write a single large shader that is compiled
21
There are four possibilities for each of the six light “slots”—point, directional, spot,
and none. This is known in combinatorics as a case of “combinations with repetitions
allowed.” In this case, there are 84 possible combinations of lights for each material, for
a total of 420 combinations.
22
As in the previous case, but one more light type increases the number of possible
light combinations per material to 210, for a total of 1050 combinations.