Adding Specular Highlights to Kart


With the background out of the way, you should take a bit of time to add some basic specular highlights to your car. A specular highlight is an area of glossiness on an object. That's the effect you are going for. To calculate this reflectivity, you not only need to know which direction the light is coming from, but you also need to know the location of the camera (or your "eye"). Add this declaration to your shader code (KartRacers.fx):

 float4 eyeVector; // Vector for the eye location 

Naturally, you update your rendering code as well, so find the OnFrameRender method in your C# class, and before the call to RenderScene, add the following line:

 effect.SetValue("eyeVector",new Vector4(camera.EyeLocation.X,     camera.EyeLocation.Y, camera.EyeLocation.Z,1)); 

This code ensures that the shader will know about the location of the camera when you're running the shader necessary for calculating the specular highlights. Of course, so far, you haven't even written that shader. Look at the code in Listing 23.1 and add that to your KartRacers.fx file.

Listing 23.1. A Per-Vertex Specular Shader
 //----------------------------------------------------------------------------- // Transformation with single directional light plus per vertex specular //----------------------------------------------------------------------------- struct PER_VERTEX_OUT {     float4 Position : POSITION;     float2 TexCoords : TEXCOORD0;     float4 Color : COLOR0; }; PER_VERTEX_OUT TransformSpecularPerVertex(float4 pos : POSITION, float3 normal : NORMAL, float2 uv : TEXCOORD0) {     PER_VERTEX_OUT Output = (PER_VERTEX_OUT)0;     // Transform position     Output.Position = mul(pos, worldViewProjection);     // Store uv coords     Output.TexCoords = uv;     // Transform the normals into the world matrix and normalize them     float3 transformedNormal = normalize(mul(normal, worldMatrix));     // Transform the world position of the vertex     float3 worldPosition = normalize(mul(pos, worldMatrix));     // Store the eye vector     float3 eyeDirection = normalize(eyeVector - worldPosition);     // Normalize the light direction     float3 lightDir = normalize(lightDirection);     // Store the diffuse component     float4 diffuse = saturate( dot(lightDir, transformedNormal) );     // Calculate the specular component     float3 reflection = normalize( 2 * diffuse * transformedNormal - lightDir);     float4 specular = pow(saturate(dot(reflection, eyeDirection)), 8);     // Set the combined color     Output.Color = diffuse + specular;     // Return the data     return Output; } 

Two big things should jump out at you right away. First, something that resembles a structure was declared, and second, this shader looks a lot more complicated than that last one. The structure is used as the output of the shader function instead of having a series of in parameters followed by a series of out parameters. The HLSL compiler is smart enough to use the output structure, and that is much easier to read than several out parameters. Take the transform shader you wrote earlier. It had three input parameters, two output parameters, and a return value. This way, the same parameters and outputs are centralized, so you have three input parameters and a single returned structure.

Inside the shader code itself, things start out the same as they did in the last shader. You transform the position, you store the texture coordinates, and then you transform the normal. Here's where things start changing, though. Notice how you've transformed the normal against the world matrix, not the combined world, view, projection matrix? The specular calculation requires that the normal be transformed into world space but not into view or projection space, which is why this part is required.

You then transform the vertex into world space alone and calculate an eye direction (by subtracting the eye location from the transformed world position). Then, you calculate the diffuse directional light color (much as you did before) and combine that with a specular component, which is calculated by the reflection of the directional light and your eye.

After you have the calculations, you store the color (the diffuse added with the specular component) and return the output structure. Because this specular code is designed to run in a per-vertex lighting environment, you aren't required to change the pixel shader you have from earlier. It is perfectly valid still. You need to add a new technique to render this effect, so in your KartRacers.fx file, add the following technique to the end:

 technique RenderSceneSpecular {     pass P0     {         VertexShader = compile vs_1_1 TransformSpecularPerVertex();         PixelShader = compile ps_1_1 TextureColor();     } } 

To use this effect now, go to the RenderScene method and switch the technique being used to this new one:

 effect.Technique = "RenderSceneSpecular"; 

Running the application now, you should see the specular highlights rendered on everything. Everything! You probably don't want to see these highlights being rendered on the road, so instead of rendering the entire scene with the same technique, you should split it between the two techniques you already have defined. Replace the RenderScene method with the one in Listing 23.2.

Listing 23.2. Updating Your Rendering Code
 private void RenderScene(Matrix world, Matrix view, Matrix proj) {     // Update the effect's variables. Instead of using strings, it would     // be more efficient to cache a handle to the parameter by calling     // Effect.GetParameter     effect.SetValue("worldViewProjection", world * view * proj);     effect.SetValue("worldMatrix", world);     effect.SetValue("lightDirection", LightDirection);     effect.Technique = "RenderSceneSpecular";     int passes = effect.Begin(0);     for (int pass = 0; pass < passes; pass++)     {         effect.BeginPass(pass);         // Render the karts         for (int kart = 0; kart < kartMeshes.Length; kart++)         {              Matrix kartMatrix = Matrix.Translation(kart * -5.5f, 0,                                                     kart * -40.0f);              effect.SetValue("worldViewProjection", kartMatrix *                  world * view * proj);                  for (int i = 0; i < kartTextures[kart].Length; i++)                  {                      effect.SetValue("SceneTexture", kartTextures[kart][i]);                      effect.CommitChanges();                      kartMeshes[kart].DrawSubset(i);                  }         }         effect.EndPass();     }     effect.End();     // Now render the track without specular highlights     effect.Technique = "RenderScene";     passes = effect.Begin(0);     for (int pass = 0; pass < passes; pass++)     {         effect.BeginPass(pass);         // Move the track up some         Matrix worldMatrix = Matrix.Translation(0, 4.5f, 0);         effect.SetValue("worldViewProjection", worldMatrix *             world * view * proj);         // Render the track         for (int i = 0; i < trackTextures.Length; i++)         {             effect.SetValue("SceneTexture", trackTextures[i]);             effect.CommitChanges();             trackMesh.DrawSubset(i);         }         effect.EndPass();     }     effect.End(); } 

The important thing to notice here is that the effect's technique is switched after rendering the go-karts and before rendering the track. The code is similar between the two renderings other than that. With that, the specular highlights are only on the karts, but because the karts have many "flat" areas, it doesn't look as realistic as it should.



Beginning 3D Game Programming
Beginning 3D Game Programming
ISBN: 0672326612
EAN: 2147483647
Year: 2003
Pages: 191
Authors: Tom Miller

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net