To address this issue, you can make the highlights appear per pixel rather than per vertex. Doing so requires a more complicated pixel shader. Back in your KartRacers.fx file, add the new shader functions in Listing 23.3. Listing 23.3. A Per-Pixel Shader//----------------------------------------------------------------------------- // Transformation with single directional light plus per pixel specular //----------------------------------------------------------------------------- struct PER_PIXEL_OUT { float4 Position : POSITION; float2 TexCoords : TEXCOORD0; float3 LightDirection : TEXCOORD1; float3 Normal : TEXCOORD2; float3 EyeWorld : TEXCOORD3; }; PER_PIXEL_OUT TransformSpecularPerPixel(float4 pos : POSITION, float3 normal : NORMAL, float2 uv : TEXCOORD0) { PER_PIXEL_OUT Output = (PER_PIXEL_OUT)0; // Transform position Output.Position = mul(pos, worldViewProjection); // Store uv coords Output.TexCoords = uv; // Store the light direction Output.LightDirection = lightDirection; // Transform the normals into the world matrix and normalize them Output.Normal = normalize(mul(normal, worldMatrix)); // Transform the world position of the vertex float3 worldPosition = normalize(mul(pos, worldMatrix)); // Store the eye vector Output.EyeWorld = normalize(eyeVector - worldPosition); // Return the data return Output; } float4 TextureColorPerPixel( float2 uvCoords : TEXCOORD0, float3 lightDirection : TEXCOORD1, float3 normal : TEXCOORD2, float3 eye : TEXCOORD3) : COLOR0 { // Normalize our vectors float3 normalized = normalize(normal); float3 light = normalize(lightDirection); float3 eyeDirection = normalize(eye); // Store our diffuse component float4 diffuse = saturate(dot(light, normalized)); // Calculate specular component float3 reflection = normalize(2 * diffuse * normalized - light); float4 specular = pow(saturate(dot(reflection, eyeDirection)), 8); float4 textureColorFromSampler = tex2D(SceneSampler, uvCoords); // Return the combined color return textureColorFromSampler * diffuse + specular; }; You'll notice here that the code is generally the same as that in the per-vertex version, but this time, it is split across two methods. Because the pixel shader cannot retrieve much of the data that the vertex shader can, you'll notice that the only job the vertex shader has here is to pass the data on to the pixel shader in the form of texture coordinates. The pixel shader then takes that data and performs the same operation the pixel shader did earlier. Once again, you need a new technique to access this new effect. Because this pixel shader is more complicated, you need at least a 2.0 shader to execute it: technique RenderSceneSpecularPerPixel { pass P0 { VertexShader = compile vs_1_1 TransformSpecularPerPixel(); PixelShader = compile ps_2_0 TextureColorPerPixel(); } } You'll notice that ps_2_0 is used to signify the updated shader required. However, currently you don't know whether your device even supports this shader model. Because you've already decided to support a minimum of vs_1_1 in the IsDeviceAcceptable method, and you already have techniques that use this version, you should only use the 2.0 version if it's supported. It's like an "added feature." Add a new variable to your C# code to decide when to use this version: // Can the device use pixel shader 2.0? private bool canUse20Shaders = false; This line defaults to false, so the advanced shaders aren't forced on a user that can't support them. However, if the user can support them, you want them to be used, so find the OnCreateDevice method and add this check to the end: if (sampleFramework.DeviceCaps.PixelShaderVersion >= new Version(2, 0) ) { canUse20Shaders = true; } Finally, you need to update the rendering method so the kart rendering uses the correct technique, depending on which model is available: effect.Technique = canUse20Shaders ? "RenderSceneSpecularPerPixel" : "RenderSceneSpecular"; With that, you should be able to see your kart being rendered with per-pixel specular highlights, as shown in Figure 23.1. Figure 23.1. Specular highlights. |