Saving the World Through Full Illumination Equations

[I posted this a while ago to - SRH]

Typical illumination equations have lots of different factors for their components. Almost all of these factors can easily be color vectors, but are instead implemented as real-valued scalars.

There's little savings in term of rendering time or space by using real scalars rather than color vectors. By the time you get to the point that you are calculating illumination values, doing a few more multiplies isn't going to kill you. Ditto for storage of surface & light properties.

For example, lots of illumination equations are implemented like this:

C = IaKd + Ii [Kd * cos(theta) + Kd * cosn(alpha)] + ItKd + IrKd


C = resulting color (color)
Ia = ambient light factor (real)
Ii = incident light power (real)
It = transparent light factor (real)
Ir = reflected light factor (real)
Kd = diffuse color (color)
n = phong specular factor (real)

This is a decent equation, but the illumination has a lot of limitations. Some people change this equation to

C = IaKd + Ii [Kd * cos(theta) + Ks * cosn(alpha)] + ItKs + IrKs

So that the specular color can be different from the diffuse color. Also, colored light sources can be accounted for by making Ii color-valued rather than real valued.

But why not go whole hog and make everything color valued so that the user has complete flexibility over illumination? For example, consider

C = IaKa + Ii [Kd * cos(theta) + Ks * cosn(alpha)] + ItKt + IrKs

C = resulting color (color)
Ia = ambient light factor (color)
Ii = incident light (color)
It = transparent light (color)
Ir = reflected light (color)
Ka = ambient surface color (color)
Kd = diffuse surface color (color)
Ks = specular surface color (color)
Kt = transparent color (color)
n = phong specular factor (color) // Maybe even this!
It = transparent light (color)

This equation is much more flexible than the ones typically found in textbooks (and many renderers). Consider the ambient illumination:

Ia set to grey level, Ka set to Kd
Allows for global setting of ambient light level, related to object's diffuse color.
Ia set to color of nearby object, Ka set to Kd
Can be used to crudely fake color-bleeding from nearby object.
Ia set to some color, Ka set to Kd.
Can be used to simulate an ambient colored light.
Ia set to grey level, Ka set to grey level.
Raise value of "black" scene color to some level of grey.
Ia set to grey level, Ka set to some color.
Tint the whole scene to some color.

Also, having Ks be separate from Kd separate from Ii allows for a lot of flexibility in the reflections of surfaces:

Ia set to some color, Ks set to Kd
Reflected light takes on the diffuse color of the object; this simulates a metallic sort of surface.
Ia set to some color, Ks set to grey level
Reflected light depends on color of the incident light; this simulates a plastic or glass surface that reflects the same light color.

Goofing around with Kt also allows one to simulate objects of a common material color, or objects that have a colored thin surface.

In fact, while writing this article, it also occurred to me that you could get some neat effects by having a color vector of Phong exponents, to have different falloff curves for each color component. Perhaps this could be used to fake irridescent reflection.

In short, if you're implementing a renderer, go ahead and make most values color vectors rather than real scalars. Simpler illumination equations can always be invoked by setting some color factor to a grey level, but color vectors allows a much greater degree of flexibility.

Also, I like to eliminate range checking on different factors unless some component values are problematic (e.g., might induce division by zero errors), or if you make simplifying assumptions for one reason or another. Eliminating unnecessary range coersion also allows the user to do things you might not have originally thought of (like darkbulbs) without any modification to your renderer. Of course, the final RGB values must be clamped to the proper range.

Steve Hollasch