The main improvement of RacorX7 over RacorX6 is the usage of a specular reflection model instead of the diffuse reflection model used by the previous example.
RacorX7 sets the same textures and texture stage states as RacorX6. It only sets one additional constant.
Define Constants (with SetPixelShaderConstant()/def)
Because this example uses a specular reflection model an eye vector must be set into c24 in FrameMove() as already shown in the RacorX4 example:
// eye vector m_pd3dDevice->SetVertexShaderConstant(24, &vEyePt, 1);
This eye vector helps to build up the V vector, which describes the location of the viewer. This is shown in the next paragraph.
Pixel Shader Instructions
The pixel shader in this example calculates the specular reflection on the basis of a modified Blinn-Phong reflection model that was already used in RacorX4 in part 2 of this introduction.
Here is the diagram that visualizes the vectors involved in the common specular reflection models:
A model describing a specular reflection has to be aware of at least the location of the light source L, the location of the viewer V, and the orientation of the surface normal N. Additionally a vector R that describes the direction of the reflection might be useful. The half way vector H was introduced by Jim Blinn to bypass the expensive calculation of the R vector in the original Phong formula.
The original Blinn-Phong formula for the specular reflection looks like this:
kspecular (N dot H)n)
H = (L + V)/2
The simplified formula that was used in RacorX4 and will be used here is
kspecular (N dot (L + V))n)
Compared to the Blinn-Phong formula the specular reflection formula used by the examples in this introduction does not divide L + V through 2. This is compensated by a higher specular power value, which leads to good visual results. Although L + V is not equivalent to H, the term Half vector is used throughout the upcoming examples.
The calculation of the vector H works in a similar way as the calculation of the light vector L in the previous example in the vertex shader:
vs.1.1 ... ; position in world space m4x4 r2, v0, c0 ; get a vector toward the camera/eye add r2, -r2, c24 ; normalize eye vector dp3 r11.x, r2.xyz, r2.xyz rsq r11.xyz, r11.x mul r2.xyz, r2.xyz, r11.xyz add r2.xyz, r2.xyz, -c12 ; get half angle ; normalize half angle vector dp3 r11.x, r2.xyz, r2.xyz rsq r11.xyz, r11.x mul r2.xyz, r2.xyz, r11.xyz ; transform the half angle vector into texture space dp3 r8.x,r3,r2 dp3 r8.y,r4,r2 dp3 r8.z,r5,r2 ; half vector -> oD1 mad oD1.xyz, r8.xyz, c33, c33 ; multiply by a half to bias, then add half
The first three code blocks caculate V. The next two code blocks generate the half vector H. H is transformed into texture space with the three dp3 instructions like the light vector in the previous example and it is stored biased in oD1 in the same way as the light vector was transfered in the previous example.
Like in the previous example, this pixel shader is driven by the vertex shader:
ps.1.1 tex t0 ; color map tex t1 ; normal map dp3 r0,t1_bx2,v1_bx2; ; dot(normal,half) mul r1,r0,r0; ; raise it to 32nd power mul r0,r1,r1; mul r1,r0,r0; mul r0,r1,r1; ; assemble final color mul r0,t0,r0 ps.1.4 texld r0, t0 ; color map texld r1, t1 ; normal map dp3 r2, r1_bx2, v1_bx2 ; dot(normal, half) mul r3,r2,r2 ; raise it to 32nd power mul r2,r3,r3 mul r3,r2,r2 mul r2,r3,r3 mul r0, r0, r2
The pixel shader gets H via v1 and N, as in the previous example, via a normal map. The specular power is caculated via four mul instructions. This was done in RacorX4 in part 2 via a lit instruction in the vertex shader.
Using the mul instructions to perform the specular color leads to visible banding artifacts:
These artifacts are a result of the small precision of the pixel shader registers and the precision loss of the H vector by shifting this vector from the range [-1..1] to [0..1] in the vertex shader and back in the pixel shader, which is necessary because the vertex shader output registers deliver only values in the range [0..1]. The only way to provide higher precision is delivering a texture map with the specular values encoded, which is loaded in the pixel shader. This will be shown below in RacorX8.
This example showed one way to implement a specular reflection model. The weak point of this example is its low precision when it comes to the specular power values. The next example will show a way to get a higher precision specular reflection.