Ambient light is modeled by giving every vertex the same colour value;
List of Standard Semantics
List of input and output Semantics
float4x4 view_proj_matrix;
float4 Light_Ambient;
struct VS_OUTPUT
{
float4 Pos: POSITION;
float3 Color: COLOR0;
};
VS_OUTPUT vs_main(float4 inPos: POSITION)
{
VS_OUTPUT Out=(VS_OUTPUT)0;
// Compute the projected position and send out the texture coordinates
Out.Pos = mul(inPos,view_proj_matrix );
// Start with the ambient color
float4 Color = Light_Ambient;
// Output Final Color
Out.Color=Color;
return Out;
}
float4 ps_main(float4 inColor: COLOR0) : COLOR
{
return inColor;
}
technique TransformTechnique
{
pass P0
{
vertexShader = compile vs_2_0 vs_main();
pixelShader = compile ps_2_0 ps_main();
}
}
protected override void Draw(GameTime gameTime)
{
graphics.GraphicsDevice.Clear(Color.CornflowerBlue);
graphics.GraphicsDevice.RenderState.CullMode = CullMode.CullCounterClockwiseFace;
// TODO: Add your drawing code here
effect.Parameters["view_proj_matrix"].SetValue(world * view * proj);
effect.Parameters["Light_Ambient"].SetValue(new Vector4(0.4f, 0.4f, 0.4f, 1));
effect.Begin();
foreach (EffectPass pass in effect.CurrentTechnique.Passes)
{
pass.Begin();
graphics.GraphicsDevice.DrawUserPrimitives(
PrimitiveType.TriangleList,
toruspoints,
0,
toruspoints.Length / 3);
pass.End();
}
effect.End();
base.Draw(gameTime);
}
Use theLambert model to render diffuse reflection. It is the dot product of the normal and light vectors. the position of the light and the normal of the vertex needs to be known.
The trick here is to model the lighting after the world transformation has been applied to the model. The simples way is to transform the light into model space using the inverse of the world matrix.
float4x4 view_proj_matrix; float4 Light_Ambient; float4 Light1_Position; float4x4 inv_world_matrix; struct VS_OUTPUT { float4 Pos: POSITION; float3 Color: COLOR0; }; VS_OUTPUT vs_main(float4 inPos: POSITION, float3 inNormal: NORMAL) { VS_OUTPUT Out=(VS_OUTPUT)0; // Compute the projected position and send out the texture coordinates Out.Pos = mul(inPos,view_proj_matrix ); // the normals may not be normalised inNormal=normalize(inNormal); // Start with the ambient color float4 Color =Light_Ambient; // Determine the light vector // first get the light vector in object space vector obj_light=mul(Light1_Position,inv_world_matrix); vector LightDir = normalize(obj_light - inPos); // Diffuse using Lambert float DiffuseAttn = max(0, dot(inNormal, LightDir) ); // Compute final lighting // assume white light vector light={0.8,0.8,0.8,1}; Color += light*DiffuseAttn; // Output Final Color Out.Color=Color; return Out; } float4 ps_main(float4 inColor: COLOR0) : COLOR { return inColor; } technique TransformTechnique { pass P0 { vertexShader = compile vs_2_0 vs_main(); pixelShader = compile ps_2_0 ps_main(); } }
protected override void Draw(GameTime gameTime)
{
graphics.GraphicsDevice.Clear(Color.CornflowerBlue);
graphics.GraphicsDevice.RenderState.CullMode = CullMode.CullCounterClockwiseFace;
// TODO: Add your drawing code here
effect.Parameters["view_proj_matrix"].SetValue(world * view * proj);
effect.Parameters["Light1_Position"].SetValue(new Vector4(0,30,0,1));
effect.Parameters["Light_Ambient"].SetValue(new Vector4(0.1f, 0.1f, 0.1f, 1));
effect.Parameters["inv_world_matrix"].SetValue(Matrix.Invert(world));
effect.Begin();
foreach (EffectPass pass in effect.CurrentTechnique.Passes)
{
pass.Begin();
graphics.GraphicsDevice.DrawUserPrimitives(
PrimitiveType.TriangleList,
toruspoints,
0,
toruspoints.Length / 3);
pass.End();
}
effect.End();
base.Draw(gameTime);
}
To model specular reflection, we will use a variation of Phong specular reflection call Blinn-Phong reflection. Traditional Phong reflection calculates the angle (phi) between the reflection vector (R) and the view vector (V), but R is expensive to calculate.
We can calculate the average of the view vector and the light vector (L), this is called the half vector(H). Blinn observed that phi was always twice the angle between the normal (N) and the half vector. As the half vector is much easier to calculate, this angle is often used instead of phi.
Incorporating Blinn-Phong is easy, the only extra infomation we need is the view position;
VS_OUTPUT vs_main(float4 inPos: POSITION, float3 inNormal: NORMAL)
{
VS_OUTPUT Out=(VS_OUTPUT)0;
// Compute the projected position and send out the texture coordinates
Out.Pos = mul(inPos,view_proj_matrix );
// the normals may not be normalised
inNormal=normalize(inNormal);
// Start with the ambient color
float4 Color =Light_Ambient;
// Determine the light vector
// first get the light vector in object space
vector obj_light=mul(Light1_Position,inv_world_matrix);
vector LightDir = normalize(obj_light - inPos);
// Determine the eye vector
// first get the eye vector in object space
vector obj_eye=mul(view_position,inv_world_matrix);
vector EyeDir = normalize(obj_eye-inPos);
// Compute half vector
vector HalfVect = normalize(LightDir+EyeDir);
// Specular, using Blinn Phong
float SpecularAttn = max(0,pow( dot(inNormal, HalfVect),64));
// Diffuse using Lambert
float DiffuseAttn = max(0, dot(inNormal, LightDir) );
// Compute final lighting
// assume white light
vector light={1,1,1,1};
Color += light*SpecularAttn+light*DiffuseAttn;
// Output Final Color
Out.Color=Color;
return Out;
}
protected override void Draw(GameTime gameTime)
{
graphics.GraphicsDevice.Clear(Color.CornflowerBlue);
graphics.GraphicsDevice.RenderState.CullMode = CullMode.CullCounterClockwiseFace;
// TODO: Add your drawing code here
effect.Parameters["view_proj_matrix"].SetValue(world * view * proj);
effect.Parameters["Light1_Position"].SetValue(new Vector4(0,30,0,1));
effect.Parameters["view_position"].SetValue(eye);
effect.Parameters["Light_Ambient"].SetValue(new Vector4(0.1f, 0.1f, 0.1f, 1));
effect.Parameters["inv_world_matrix"].SetValue(Matrix.Invert(world));
effect.Parameters["world_matrix"].SetValue(world);
effect.Begin();
foreach (EffectPass pass in effect.CurrentTechnique.Passes)
{
pass.Begin();
graphics.GraphicsDevice.DrawUserPrimitives(
PrimitiveType.TriangleList,
toruspoints,
0,
toruspoints.Length / 3);
pass.End();
}
effect.End();
base.Draw(gameTime);
}
Standard fixed function pipelines use gouraud shading, calculating lighting at each vertex and interpolating the lighting colour across the pixels. A more accurate way to do lighting is Phong shading; normal, half, and light vectors are interpolated across pixels and the lighting is calculated from scratch for each pixel.
In shader programming, this means moving the lighting calculation to the pixel shader.
The lighting calculation needs normal, half, and light vectors, these are calculated for each vertex in the vertex shader.
The only input semantics for a pixel shader are color and texturecoordinates, there are no semantics for other vectors. We will pass the normal, half and light vectors via the texturecoordinate semantics.
float4x4 view_proj_matrix;
float4 Light_Ambient;
float4 Light1_Position;
float4 Light1_Color;
float3 view_position;
float4x4 inv_world_matrix;
struct VS_OUTPUT_PER_PIXEL
{
float4 Pos: POSITION;
float3 normal: TEXCOORD1;
float3 light: TEXCOORD2;
float3 halfvect: TEXCOORD3;
float3 Color: COLOR0;
};
vector lighting(vector color, float3 normal, float3 light_dir, float3 half_vect){
// Output the lit color
// Specular
float SpecularAttn = max(0,pow( dot(normal, half_vect),32));
// Diffuse
float DiffuseAttn = max(0, dot(normal, light_dir) );
// Compute final lighting
color *= (SpecularAttn+DiffuseAttn);
return color;
}
VS_OUTPUT_PER_PIXEL vs_main_per_pixel(float4 inPos: POSITION, float3 inNormal: NORMAL)
{
VS_OUTPUT_PER_PIXEL Out=(VS_OUTPUT_PER_PIXEL)0;
// Compute the projected position and send out the texture coordinates
Out.Pos = mul(inPos,view_proj_matrix );
inNormal=normalize(inNormal);
// Output the ambient color
float4 Color =Light_Ambient;
// Determine the eye vector
vector obj_eye=mul(view_position,inv_world_matrix);
vector EyeDir = normalize(obj_eye-inPos);
vector obj_light=mul(Light1_Position,inv_world_matrix);
vector LightDir = normalize(obj_light - inPos);
// Compute half vector
vector HalfVect = normalize(LightDir+EyeDir);
// Output Final Color
Out.Color=Color;
Out.normal= inNormal;
Out.light= LightDir;
Out.halfvect=HalfVect;
return Out;
}
float4 ps_main_per_pixel(
float4 inColor: COLOR0,
float3 inNormal: TEXCOORD1,
float3 LightDir: TEXCOORD2,
float3 HalfVect: TEXCOORD3
) : COLOR
{
return lighting(inColor,inNormal,LightDir,HalfVect);
}
technique TransformPerPixel
{
pass P0
{
vertexShader = compile vs_2_0 vs_main_per_pixel();
pixelShader = compile ps_2_0 ps_main_per_pixel();
}
}
© Ken Power 2010