Basic Lighting with a shader

Ambient Light

Ambient light is modeled by giving every vertex the same colour value;

 

List of Standard Semantics

List of input and output Semantics

Shader Code

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();
    }
}


C# Code

        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);
        }
 

Diffuse reflection

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.

Shader Code

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();
    }
}

C# Code

       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);
        }

Specular reflection

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;

Shader Code

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;
}

C# Code

        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);
        }
 

Per-Pixel Lighting

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();
    }
}

Source Code of above Project