Per Pixel Specular

I've been trying various ways to get per pixel specular bump mapping with per pixel normalization.

There are many articles explaning how to do power function approximation, specular power lookups, etc. These either give results with poor precision, or require dependant texture reads to get per pixel normalization.

Then I came across a note saying that you could do the exponent in the normalization cube map. So I took my cube map in photoshop, duplicated the layer, set blend mode to multiplication, repeated a few times and raised it the 32nd power.

Now I get a smooth specular highlight, with fewer pixel shader instructions, no precision problems on older cards, no dependant reads, and just plain looks better than the other techniques I've tried.

Code:
ps.1.1
tex t0		   // Base texture
tex t1		   // Normal from bump map
tex t2		   // Normalization cube map with exponent
dp3 r0,t2_bx2,t1_bx2 // Dot product half angle and bump, normalize and exponent from cube map
mul r0,r0,t0	   // Multiply by base texture
mul r0, r0, t1.a	   // * gloss value

The only downside is the exponent is precalculated. If I want to change the how much an object shines I need a different cube map. However 3 or 4 of them may be enough to give different looks to objects in a game. You can also multiply the specular by a factor per light, along with the per pixel gloss map to get different surfaces.
 
Here is an image showing the difference between methods:


PerPixelSpecular.jpg


The image on the left used a series of multiplies in pixel shader to raise the exponent, and suffers from banding.
 
K.I.L.E.R:

PS 2.0 can use the pow function. However it still suffers from banding (at least on this Radeon 9700).

Just doing a plain translation on the shader from PS 1.1 to PS 2.0 gives 2-3 fps slower results. The PS 2.0 shader with power function needs more instructions as well.

The exponent in the cube map method can be used in PS 2.0, and can be combined with other code easily (ie diffuse+specular in 1 pass). Right now I'm using the pow function, however I will switch to the new method to be consistent and get the same results on PS 1.1 and PS 2.0.

The engine is going to run on GF3, so having most of the shaders in PS 1.1 is important.
 
Back
Top