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
where:
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:
Also, having Ks be separate from Kd separate from Ii allows for a lot of flexibility in the reflections of surfaces:
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.