For simulating shiny/polished surfaces the algorithm must also take into account the location of the viewer.

There are several lighting models for simulating specular reflectance. We will use the Phong specular lighting model developed by Bui Tong Phong.
We will use a reflection vector that contains the direction of the light after it has bounced off
the surface. This vector is calculated from the light direction vector and the normal of the
surface:

The reflection vector

This reflection vector **R** is compared to the view direction vector. If the angles are similar, the surface will be lit brightly, if they are far off, no specular light is reflected from the surface into the camera and only the diffuse and ambient light will be visible on this pixel.

The angle between the reflection vector and view vector.

The reflection vector **R** is calculated with the following formula:

** R = 2 * (N · L) * N L**

The view vector **V** is calculated by subtracting the vector world position from the camera position:

** V = Camera Position - Vertex Position**

This results in a vector pointing from the vertex to the camera (see "Subtracting Vectors" in
Appendix B). As with diffuse lighting, we will use the dot product to calculate the cosine of the angle difference between the two vectors. This time we need the difference between the view vector and the reflection vector. We then raise the result to a power **n** which makes the highlight harder or softer.

**Specular Light = (R · V) ^{n} **

Our entire equation is now:

**Final Color = (Diffuse Light + Ambient Light + Specular Light) * Diffuse Color **

We have added two new variables:// Tweakables:static const float AmbientIntensity = 1.0f;// The intensity of the ambient light.static const float DiffuseIntensity = 1.0f;// The intensity of the diffuse light.static const float SpecularIntensity = 2.0f;// The intensity of the specular light.static const float SpecularPower = 8.0f;// The specular power, used as 'glossiness' factor.static const float4 SunColor = {0.9f, 0.9f, 0.5f, 1.0f};// Color vector of the sunlight.// Application fed data:const float4x4 matWorldViewProj;// World*view*projection matrix.const float4x4 matWorld;// World matrix.const float4 vecAmbient;// Ambient color.const float4 vecSunDir;// The sun light direction vector.const float4 vecViewPos;// View position.texture entSkin1;// Color map.sampler ColorMapSampler = sampler_state// Color map sampler.{ Texture = <entSkin1>; MipFilter = Linear;// required for mipmapping

};// Vertex Shader:void SpecularVS( in float4 InPos: POSITION, in float3 InNormal: NORMAL, in float2 InTex: TEXCOORD0, out float4 OutPos: POSITION, out float2 OutTex: TEXCOORD0, out float3 OutNormal: TEXCOORD1, out float3 OutViewDir: TEXCOORD2) {// Transform the vertex from object space to clip space:OutPos = mul(InPos, matWorldViewProj);// Transform the normal from object space to world space:OutNormal = normalize(mul(InNormal, matWorld));;// Pass the texture coordinate to the pixel shader:OutTex = InTex;// Calculate a vector from the vertex to the view:OutViewDir = vecViewPos - mul(InPos, matWorld); }// Pixel Shader:float4 SpecularPS( in float2 InTex: TEXCOORD0, in float3 InNormal: TEXCOORD1, in float4 InViewDir: TEXCOORD2) : COLOR {// Calculate the ambient term:float4 Ambient = AmbientIntensity * vecAmbient;// Calculate the diffuse term:InNormal = normalize(InNormal); float4 Diffuse = DiffuseIntensity * SunColor * saturate(dot(-vecSunDir, InNormal));// Fetch the pixel color from the color map:float4 Color = tex2D(ColorMapSampler, InTex);// Calculate the reflection vector:float3 R = normalize(2 * dot(InNormal, -vecSunDir) * InNormal + vecSunDir);// Calculate the speculate component:float Specular = pow(saturate(dot(R, normalize(InViewDir))), SpecularPower) * SpecularIntensity;// Calculate final color:return (Ambient + Diffuse + Specular) * Color; }// Technique:technique SpecularTechnique { pass P0 { VertexShader = compile vs_2_0 SpecularVS(); PixelShader = compile ps_2_0 SpecularPS(); } }

We have added one output parameter to the vertex shader which will contain a vector pointing from the vertex to the view; we call it "// Vertex Shader:void SpecularVS( in float4 InPos: POSITION, in float3 InNormal: NORMAL, in float2 InTex: TEXCOORD0, out float4 OutPos: POSITION, out float2 OutTex: TEXCOORD0, out float3 OutNormal: TEXCOORD1, out float3 OutViewDir: TEXCOORD2) {// Transform the vertex from object space to clip space:OutPos = mul(InPos, matWorldViewProj);// Transform the normal from object space to world space:OutNormal = normalize(mul(InNormal, matWorld));;// Pass the texture coordinate to the pixel shader:OutTex = InTex;// Calculate a vector from the vertex to the view:OutViewDir = vecViewPos - mul(InPos, matWorld); }

The specular highlights are dependent on the view direction so we have to calculate a vector that gives the view direction. We know the world position of the view (

// Pixel Shader:float4 SpecularPS( in float2 InTex: TEXCOORD0, in float3 InNormal: TEXCOORD1, in float4 InViewDir: TEXCOORD2) : COLOR {// Calculate the ambient term:float4 Ambient = AmbientIntensity * vecAmbient;// Calculate the diffuse term:InNormal = normalize(InNormal); float4 Diffuse = DiffuseIntensity * SunColor * saturate(dot(-vecSunDir, InNormal));// Fetch the pixel color from the color map:float4 Color = tex2D(ColorMapSampler, InTex);// Calculate the reflection vector:float3 R = normalize(2 * dot(InNormal, -vecSunDir) * InNormal + vecSunDir);// Calculate the speculate component:float Specular = pow(saturate(dot(R, normalize(InViewDir))), SpecularPower) * SpecularIntensity;// Calculate final color:return (Ambient + Diffuse + Specular) * Color; }

We receive the view direction vector in **TEXCOORD2** where we put it in the pixel shader. The ambient and diffuse components are calculated like before.
We calculate the reflection vector **R** with the formula discussed above. The specular
component is calculated by taking the dot product of the reflection vector and the view
direction, raised to the power given by the **SpecularPower** variable making the highlight
harder or softer. The result is multiplied by **SpecularIntensity** to make the highlight brighter or
darker.

We once again add up all the light, now including specular and multiply it by the color which
is read from the color map. Once again, start SED, open **speculardemo.c** and run it:

By adding specular highlights, we see a view dependent highlight making the material look shiny and polished. To make the effect more clear we've added a line to the script that lets the object slowly rotate.