Many months ago I spent several days coming up with a decent way to do specular materials. My old lighting scheme was diffuse only, and things looked quite flat, so I decided to update the material system to support specular, and more specifically, metallic shading.
One interesting thing about metals is they reflect their own light color, rather than the light color coming in, like
plastics would.
So, to get a decent metal, there needs to be some sort of color shift during the lighting computation.
Also, because of the top-down perspective, standard N.H lighting did not look good on the flat surfaces of the floor. Since the floor is 90% of what you see in the game, that wasn't going to work.
Additionally, I didn't want to have to use ps.2.0 hardware in order to do decent looking specular, so I came up with a series of techniques that I feel does a good job with specular materials.
The first thing I did was to change to an N.E lighting model instead of N.H. The N.E model works better on the floor, and
it's also more linear than N.H, so it doesn't wobble as much.
The next thing was to get a stable highlight. The trick here was not to use an exponent, which can't be done well or cheaply
in ps.1.1 hardware, at least if you are trying to do diffuse & specular all in a single pass. The trick was to use the
'bumpheight' scaling of the E vector. A pretty standard tweak to bump mapping allows you to scale the tangent space light
vector ( or in this case E vector ) anisotropically, then renormalize. For instance, you might scale the tangent space Z
direction ( out from the surface ) up to make the surface more flat looking and less bumpy.
Similarly, scaling L.z down then renormalizing, emphasizes any bumps in your surface, because the light more often and
more intensely seems to be coming from the side.
This allows a highlight-type effect with just a simple scale in the vertex shader. I use cubemaps to renormalize my L and E vectors for the lighting.
The next step is to do a color shift necessary to achieve a metallic effect.
I mark the specular sections of the texture in the alpha channel, modulate this by a global reflectivity parameter,
then by half the material's specular color.
Why Half? Well, this is where the color shift comes in. To pass a value to a ps.1.1 pixel shader as a constant, it must
be in the [-1..1] range. If you want a color shift, one way to do it is to do :
color *= { 0.6f, 1.5f, 0.8f };
What this does is to boost the green channel more, while having standard values in the other color channels. In order to
fit values up to 2.0 in the constant, you have to divide your color down by 2, then pass it in, then do an x2 in the pixel shader.
This allows you do to overbright colors in general, and also one type of color shift. A more general solution would have a lookup 3d texture to translate one color to another.
Here are two shots of a statue in silver & gold.
I forget where it was now, but there were some slides delivered at one of the conferences (GDC/SIGGRAPH etc...) a couple of years back (when the very, very, first shader stuff was coming in).
They had a graphics method that doubled up as a gameplay hint - drawing a few "sparkle" particles on a couple of vertices of the model. Not only did it give the graphical impression that it was shiny/gold, but it drew the players eye to a sparkling/shiny/gold piece that they might want to pick up and keep.
Dunno if that's of any interest to you?
Cheers,
Jack