The vertex shader is really straightforward
struct a2v{ float4 position: POSITION; float3 normal: NORMAL; float2 texcoord0: TEXCOORD0;};struct v2f{ float4 position : POSITION0; float3 normal : TEXCOORD0; float3 view_vec : TEXCOORD1;};v2f main(a2v i){ v2f o; o.position = mul( i.position, world_view_proj ); o.normal = mul( i.normal, model_to_world ); o.view_vec = camera_pos - i.position; return o;}
The only thing worth mentioning is that it is sending the view vector in TEXCOORD1 to the pixel shader, this will be used to calculate the reflection vector as well as lookup into a texture that gives the object the "oily" or "soapy" look to it. The pixel shader is where the fun stuff happens, the shader requires two textures, the first is a 1D texture that gives the object the "oily" feel described before. The next texture is the cube map that will be used for the object's reflection.
sampler Rainbow: register(s0);sampler Environment: register(s1);float rainbowiness: register(c0); // 0.0 - 1.0 ( 0.5 looks good )float4 main(float3 normal: TEXCOORD0, float3 viewVec: TEXCOORD1) : COLOR { normal = normalize(normal); float v = dot(normalize(viewVec), normal); // lookup into the 1D rainbow texture float3 rainbow = tex1D(Rainbow, v); // Calculate the reflection vector, then lookup into cubemap float3 reflVec = reflect(-viewVec, normal); float3 refl = texCUBE(Environment, reflVec); // interpolate reflection color to rainbow color float3 color = lerp(refl, rainbow, rainbowiness * v); // return the interpolated color and use 1-v as transparency return float4(color, 1 - v);}
The line:
float v = dot(normalize(viewVec), normal);
The first use for the dot product between the view vector and the normal is to lookup into the 1D "rainbow" texture, what this will give us, is a smooth sampling of the texture. The texture will be sampled from left to right, the leftmost color will be used for the places where the normal is least aligned to the view vector at, and gradually up to the rightmost color at the places in which the view and the normal are most aligned. Here's a screenshot of the effect only looking into the rainbow texture:
Click for full size
The next two lines are very straightforward
// Find the reflection
float3 reflVec = reflect(-viewVec, normal);
float3 refl = texCUBE(Environment, reflVec);
All they do is calculate the reflection vector and use it to lookup the appropiate texel in the cube map.
The next use for the dot product is to calculate the transparency of the object, it works in the same way, the object will be fully opaque when the normal is least aligned to the view vector and will gradually become transparent as the view vector and normal become aligned. You can see this in the last line of the shader:
return float4(color, 1 - v);
Here's a screenshot where I hardcoded the ship's color to green just so you can see how the alpha looks:
Click for full size
Here's the rainbow texture I used:
Right click here then Save as...
The pixel shader needs to be compiled in PS2.0 because of the dependent texture read where we lookup the rainbow texture.