You can’t really do anything tangent space on two texture units, because you need three register combiners just to do a single tangent space transform (it’s three dot3 operations).
However, just doing plain phong doesn’t require tangent space transforms. If Gouraud looks like this:
out1 =
interpolate(
diffuse( v1.N, v1.L ),
diffuse( v2.N, v2.L ),
diffuse( v3.N, v3.L ) ) +
interpolate(
specular(
reflect( v1.N, v1.E ),
v1.L ),
specular(
reflect( v2.N, v2.E),
v2.L ),
specular(
reflect( v3.N, v3.E),
v3.L ) );
then there’s two “vector based” interpolators, one of which is “phong” (I forget which):
out2 =
diffuse(
interpolate( v1.N, v2.N, v3.N ),
interpolate( v1.L, v2.L, v3.L ) ) +
specular(
interpolate(
reflect( v1.N, v1.E ),
reflect( v2.N, v1.E ),
reflect( v3.N, v1.E ) ),
interpolate( v1.L, v2.L, v3.L ) );
out2 =
diffuse(
interpolate( v1.N, v2.N, v3.N ),
interpolate( v1.L, v2.L, v3.L ) ) +
specular(
reflect(
interpolate( v1.N, v2.N, v3.N ),
interpolate( v1.E, v2.E, v3.E ) ),
interpolate( v1.L, v2.L, v3.L ) );
Assuming for a moment that you have an infinitely distant light, then all you need to interpolate is N and reflect( N, E ). Each of those can be calculated in software and sent down as texture coordinates in the range (0,1). These, in turn, look up in a cube map which “normalizes” the value to a unit-length RGB triple in (-128,127) space. You can bind the same cube map on both texture units.
Then your output looks like:
out2 =
dot_clamp( TEX0, L ) +
raise( dot_clamp( TEX1, L ), power ) ;
If you’re using register combiners, and use the final combiner to raise the specular value, you can probably get an exponent of 8 (IIRC). Especially if you cheat on the “power” part and add as much biasing and clamping to add as much linear fall-off as you can
Hmm, come to think of it, you can probably use NORMAL_MAP and REFLECTION_MAP texgen to actually give you the right texture coordinates, without having to calculate them in software. You’d still need the normalization, but this is a huge improvement in speed
If you want to get a diffuse color map in there, you need to do it in two passes; one to add in diffuse * colormap, and one to add in specular. At that point, you can raise specular to a higher power using the freed-up combiner, and/or use a gloss map (yay!). GF2 doesn’t allow dependent texture reads, so you can’t use a look-up texture for the specular function, though. (You can do awesome anisotropic things with that
Also note that I prefer to view specular as expressed through reflection (because that’s what it is) rather than as expressed with half-angle, but they are equivalent (modulo some n2 power of what you get out of the first dot product, I think). You can re-write in terms of half-angle just fine. I forget whether doing so GAINS you power, or LOSES power, compared to reflection.
Here’s a question, the answer to which I’m too busy to look up right now: which of out2 and out3 is “Phong” shading? And then, what’s the other one called? Or are they actually interchangeable?