XNA UK User Group

A helping hand for bedroom coders throughout the land.
in

This Blog

Syndication

RandomChaos

November 2008 - Posts

  • Basic HLSL Lighting Techniques - Episode 3: Colour, Glow, Bump and Reflective Texture Maps

    http://www.youtube.com/v/XOujFp7ZccA <p><a href="http://www.youtube.com/v/XOujFp7ZccA">http://www.youtube.com/v/XOujFp7ZccA</a></p>

    I guess the next natural step is texturing a model. In this episode I am going to cover basic texturing, then go onto what I call glow maps, then bump mapping and then reflective textures. Now ALL the models and the textures in this episode are taken directly from the DirectX 9 SDK. The only thing I have altered is the sphere.x file as it has a reference to the diffuse (colour map) texture and as I like to store my textures else where and I am also not covering the content pipeline here, I have removed the reference. Also, this is my take on how the textures should be used, and I think it works quite well, guess you guys and girls will be the best judge of that though.

    Colour Map

    Earth_Diffuse

    So, we have a model that we want to apply a texture to, in this case it's a sphere and the texture is a colour map of the earth. What you need to do is see the colour map as the resulting diffuse colour on the model, so we take the diffuse value on the model and multiply the texture colour by it to get the resulting colour required. As ever we require the World * View * Projection matrix, and the World matrix, I am also going to include ambient light here, but not pass it and just use  the default values, but this will show how you can add the earlier lighting to this effect and as we are doing diffuse lighting we need a light direction again. Now comes the new bit, two new elements, a texture and a sampler. The texture is the parameter that will hold the texture we are passing to the shader, in this case the ColorMap. The sampler is what is used in the pixel shader to reference the texture and get the required pixel from it.

    float4x4 wvp : WorldViewProjection;
    float4x4 world : World;

    float AmbientIntensity = 1;
    float4 AmbientColor : AMBIENT = float4(0,0,0,1);

    float3 LightDirection : Direction = float3(0,1,1);

    texture ColorMap : Diffuse;
    sampler ColorMapSampler = sampler_state
    {
        texture = <ColorMap>;   
    };

    VS_IN has a new member added to it, TexCoord, now your model NEEDS to have these in them or you can not (to my knowledge) apply a texture to it correctly. The TexCoord is a float2 (Vector2) value at each vert that ranges from 0 to 1 and represents a position on the texture to be applied to the model. So if a vert had the value 0.5,0.5 for it's TexCoord then this would be a pixel at the very centre of the texture. The two values are also referred to as U and V, like X and Y but for textures, hence UV mapping. Our VS_OUT structure has the usual Position, Light and Normal, but again has a TexCoord added.

    struct VS_IN
    {
        float4 Position : POSITION;
        float2 TexCoord : TEXCOORD0;
        float3 Normal : NORMAL;   
    };
    struct VS_OUT
    {
        float4 Position : POSITION;
        float2 TexCoord : TEXCOORD0;
        float3 Light : TEXCOORD1;
        float3 Normal : TEXCOORD2;   
    };
    struct PS_OUT
    {
        float4 Color : COLOR;
    };

    In our vertex shader we do all the same things we did before for the diffuse lighting only we add the bit to pass on the TexCoord to the pixel shader.

    VS_OUT VS_ColorMap(VS_IN input)
    {
        VS_OUT output = (VS_OUT)0;
        output.Position = mul(input.Position,wvp);   
        output.Light = LightDirection;
        output.TexCoord = input.TexCoord;
        output.Normal = mul(input.Normal,world);
        return output;
    }

    In our pixel shader we calculate the light direction and the amount of diffuse light as before as well as the ambient light, but we also get the current pixel for this given set of TexCoords from the sampler for this pixel and adjust it's colour by the diffused light we just calculated.  This is then added to the ambient light value to give the pixel colour.

    PS_OUT PS_ColorMap(VS_OUT input)
    {
        PS_OUT output = (PS_OUT)0;
        float3 LightDir = normalize(input.Light);
        float Diffuse = saturate(dot(LightDir,normalize(input.Normal)));
        float4 texCol = tex2D(ColorMapSampler,input.TexCoord);
        float4 Ambient = AmbientIntensity * AmbientColor;
        texCol *= Diffuse;
        output.Color =  Ambient + texCol;
        return output;
    }

    Glow Map

    Earth_Night

    That was pretty painless eh... So is this. We add another texture and sampler to the shader so we can pass in the glow map texture.

    texture GlowMap : Diffuse;
    sampler GlowMapSampler = sampler_state
    {
        texture = <GlowMap>;
    };

    The structures stay the same and we just add the glow map calculation to the pixel shader. The glow map is effectively multiplying the glow map pixel by the opposite of the calculated diffuse, we then add this to the colour to be passed to the screen making the pixel shader look like this.

    PS_OUT PS_ColorGlowMap(VS_OUT input)
    {
        PS_OUT output = (PS_OUT)0;
        float3 LightDir = normalize(input.Light);
        float Diffuse = saturate(dot(LightDir,normalize(input.Normal)));
        float4 texCol = tex2D(ColorMapSampler,input.TexCoord);
        float4 glowCol = tex2D(GlowMapSampler,input.TexCoord);
        float4 Ambient = AmbientIntensity * AmbientColor;
        float4 glow = glowCol * saturate(1-Diffuse);   
        texCol *= Diffuse;
        output.Color =  Ambient + texCol + glow;
        return output;
    }

    This gives us the following effect:

    Bump Map

    Earth_NormalMap

    Now to get bump mapping to work we have to do a few things to both the shader and the model (via the core content importer). First of all we need our model to have "Tangents". Tangents are required to generate the correct "tangent space" for the calculation of the effect, Wolfgang explains this much better than I could and I don't wish to plagiarise him here so if you would like a better description I suggest you buy the book and read chapter 7 page 111. To tell the content pipeline that we want our model to use tangents we simply select the model, expand the Content Processor and set the Generate Tangent Frame to True, it is False by default.

    Now we have done that when the model compiles (if it has the correct Vertex Channels, if it does not you will get a compile error, many posts ago I posted a workaround to this, but event hen you will still not get tangent data if the channels are not there) it will have tangent data that we can use in the shader.

    We add yet another texture map for the bump map

    texture BumpMap ;
    sampler BumpMapSampler = sampler_state
    {
        Texture = <BumpMap>;
    };

    The structures change too....

    struct VS_IN
    {
        float4 Position : POSITION;
        float2 TexCoord : TEXCOORD0;
        float3 Normal : NORMAL;
        float3 Tangent : TANGENT;
    };
    struct VS_OUT
    {
        float4 Position : POSITION;
        float2 TexCoord : TEXCOORD0;
        float3 Light : TEXCOORD1;
    };

    We have added the Tangent data to the VS_IN structure so we can use the newly generated tangent data and in the VS_OUT structure we have removed the Normal member. This is because we are going to use the world tangent space to transpose the light in the vertex shader and then use the bump (normal) map to obtain the normal.

    The vertex shader now looks like this

    VS_OUT VS_ColorGlowBump(VS_IN input)
    {
        VS_OUT output = (VS_OUT)0;
        output.Position = mul(input.Position,wvp);
        float3x3 worldToTangentSpace;
        worldToTangentSpace[0] = mul(input.Tangent,world);
        worldToTangentSpace[1] = mul(cross(input.Tangent,input.Normal),world);
        worldToTangentSpace[2] = mul(input.Normal,world);
        output.Light = mul(worldToTangentSpace,LightDirection);   
        output.TexCoord = input.TexCoord;
        return output;
    }

    As you can see, we transform the position as before, then calculate the tangent space matrix, and using that transform the light direction.

    PS_OUT PS_ColorGlowBump(VS_OUT input)
    {
        PS_OUT output = (PS_OUT)0;
        float3 Normal = (2 * (tex2D(BumpMapSampler,input.TexCoord))) - 1.0;
        float3 LightDir = normalize(input.Light);
        float Diffuse = saturate(dot(LightDir,Normal));
        float4 texCol = tex2D(ColorMapSampler,input.TexCoord);
        float4 glowCol = tex2D(GlowMapSampler,input.TexCoord);
        float4 Ambient = AmbientIntensity * AmbientColor;
        float4 glow = glowCol * saturate(1-Diffuse);   
        texCol *= Diffuse;
        output.Color =  Ambient + texCol + glow;
        return output;
    }

    In the above pixel shader we get the normal from the bump map, normalise the light direction, then calculate the diffuse as before but with the values generated from tangent space. The rest of the shader is as before giving this result.

    Reflective Map

    Earth_ReflectionMask

    Now this is as easy as the colour and the glow map implementations. What we are going to do is implement specular lighting effect but only where the reflection map tells us to. Again we add another texture and sampler to manage the reflection map.

    texture ReflectionMap : Diffuse;
    sampler ReflectionMapSampler = sampler_state
    {
        texture = <ReflectionMap>;   
    };

    We now need to add the members we require for specular lighting as we did in the last episode to the structures. VS_IN stays the same but VS_OUT needs to be modified for specular lighting.

    struct VS_OUT
    {
        float4 Position : POSITION;
        float2 TexCoord : TEXCOORD0;
        float3 Light : TEXCOORD1;
        float3 CamView : TEXCOORD2;
        float4 posS : TEXCOORD3;
        float3 Normal : TEXCOORD4;   
    };

     

    We then add our specular lighting code to the vertex shader

    VS_OUT VS_Ambient(VS_IN input)
    {
        VS_OUT output = (VS_OUT)0;
        output.Position = mul(input.Position,wvp);
        float3x3 worldToTangentSpace;
        worldToTangentSpace[0] = mul(input.Tangent,world);
        worldToTangentSpace[1] = mul(cross(input.Tangent,input.Normal),world);
        worldToTangentSpace[2] = mul(input.Normal,world);
        float4 PosWorld = mul(input.Position,world);
        output.Light = mul(worldToTangentSpace,LightDirection);   
        output.CamView = CameraPosition - mul(input.Position,world);
        output.posS = input.Position;
        output.TexCoord = input.TexCoord;
        output.Normal = mul(input.Normal,world);
        return output;
    }

    And now we apply the specular effect to the areas the reflection map tells us to in the pixel shader

    PS_OUT PS_Ambient(VS_OUT input)
    {
        PS_OUT output = (PS_OUT)0;
        float3 Normal = (2 * (tex2D(BumpMapSampler,input.TexCoord))) - 1.0;
        float3 LightDir = normalize(input.Light);
        float Diffuse = saturate(dot(LightDir,Normal));
        float4 texCol = tex2D(ColorMapSampler,input.TexCoord);
        float4 glowCol = tex2D(GlowMapSampler,input.TexCoord);
        float4 Ambient = AmbientIntensity * AmbientColor;
        float4 glow = glowCol * saturate(1-Diffuse);       
        texCol *= Diffuse;
        float3 Half = normalize(normalize(LightDirection) + normalize(input.CamView));   
        float specular = pow(saturate(dot(normalize(input.Normal),Half)),25);
        float4 specCol = 2 * tex2D(ReflectionMapSampler,input.TexCoord) * (specular * Diffuse);
        output.Color =  Ambient + texCol + glow + specCol;
        return output;
    }

    I have high lighted the import ant bit in purple and bold. We now have an effect that looks like this.

    Also, if you open up my solution and take a look at the images, in the Content Processor of each I have also set the Resize To Power of Two to True, this makes the images more graphics pipeline friendly.

    You can download the sample for this episode here.

  • Basic HLSL Lighting Techniques - Episode 2: Coloured, Specular & Multiple Lights

    http://www.youtube.com/v/Dj6V7hwVcPg <p><a href="http://www.youtube.com/v/Dj6V7hwVcPg">http://www.youtube.com/v/Dj6V7hwVcPg</a></p>

    Coloured Light

    So the last shader we did had both ambient and diffuse lighting in it. But what if your light is coloured and has a varying brightness? All we have to do is add two more parameters and alter our light calculation in the pixel shader.

    I have simply copied the existing AmbientDiffuseLight shader and re named it AmbientDiffuseColoredLight. At the top, between AmbientColor and LightDirection I have added the following

    float DiffuseIntensity = 1;
    float4 DiffuseColor : Diffuse = float4(1,1,1,1);

    The pixel shader now looks like this

    PS_OUT PS_AmbientDiffuse(VS_OUT input)
    {
        PS_OUT output = (PS_OUT)0;
        // Get ambient light
        AmbientColor *= AmbientIntensity;
        // Get diffuse light
        DiffuseColor = (DiffuseIntensity * DiffuseColor) * saturate(dot(input.Light,input.Normal));
        output.Color = AmbientColor + DiffuseColor;
        return output;
    }

    We first calculate the ambient light value, instead of putting this into another variable I have just used the global AmberColor, so multiplying its self by the intensity. Then we get the diffuse colour, multiply that by the diffuse intensity and then multiply that by our reflective light calculation. These two values are then added together and used to set our output colour.

    This shader gives the following results:-

    Gold, Red, CornflowerBlue and White Lights

       

     

    Now, what if I want multiple lights in my scene? Well we then need to add a light calc per source of light in the scene. So we will have n light positions with n colors and n positions and these values will have to be passed to the shader.

    Multiple Lights

    To create multiple lights in the shader we are going to replace the diffuse colour, intensity and light direction with an array for each. The size of the array will depend on the number of lights you want to use in the scene. To help define the arrays in the shader we will define a macro. We will create 3 lights in this sample

    So the diffuse lighting parameters now look like this

    #define MaxLights 3

    float DiffuseIntensity[MaxLights] = {1,1,1};
    float4 DiffuseColor[MaxLights] : Diffuse = {float4(1,0,0,1),float4(0,1,0,1),float4(0,0,1,1)};
    float3 LightDirection[MaxLights] : Direction = {float3(1,0,0),float3(0,1,0),float3(0,0,1)};

    We then alter the VS_OUT structure so it can store the normalized light positions. NOTE: We can do this calculation in the pixel shader, but this would be over kill (IMhO) as we don't need this done per pixel as the light direction will apply to all pixels in the face, as is the case with the normal.

    struct VS_OUT
    {
        float4 Position : POSITION;
        float3 Light[MaxLights] : TEXCOORD0;
        float3 Normal : TEXCOORD3;
    };

    You will notice the TEXCOORD semantic jumps from TEXCOORD0 for the light member to TEXCOORD3, this is because the TEXCOORD0 defines the start of the members channel, there are 3 lights so Light takes up 3 channels; TEXCOORD0-2. If you increase the number of lights in the shader you will have to increase the TEXCOORD channel of the Normal member.

    Our vertex shader is now altered to populate the light members with the normalized light directions.

    VS_OUT VS_AmbientDiffuse(VS_IN input)
    {
        VS_OUT output = (VS_OUT)0;
        output.Position = mul(input.Position,wvp);
        // Normalize the light directions
        for(int l=0;l<MaxLights;l++)
            output.Light[l] = normalize(LightDirection[l]);
        output.Normal = normalize(mul(itw,input.Normal));
        return output;
    }

    Now all we need to do is alter the diffuse light calculation in the pixel shader. NOTE: Again, we could move this calc out to the vertex shader, but the quality of the lighting would be greatly reduced.

    PS_OUT PS_AmbientDiffuse(VS_OUT input)
    {
        PS_OUT output = (PS_OUT)0;
        // Get ambient light
        AmbientColor *= AmbientIntensity;
        // Get diffuse light
        float4 TotalDiffuseColor = float4(0,0,0,0);
        for(int l=0;l<MaxLights;l++)
            TotalDiffuseColor += (DiffuseIntensity[l] * DiffuseColor[l]) * saturate(dot(input.Light[l],input.Normal));

        output.Color = AmbientColor + TotalDiffuseColor;
        return output;
    }

    And Ta Daaa! A multi light shader giving this result

    Specular Light

    Specular reflection is used to simulate smooth, shiny or wet surfaces. There are two methods (that I know of thanks to Engel) to get a specular effect, one created by Bui Tong Phong in 1975 (I was 4 that year) known as Phong Reflectance and a modification to this by Jim Blinn in 1977 known as Blinn-Phong Reflectance. I am only going to apply the latter in this post, the Blinn-Phong model and I will only apply this to a single light shader.

    As with our ambient light shader we need a WorldViewProjection matrix and we also need a world matrix, we will apply this specular effect with an ambient and diffuse light also.

    float4x4 world : World;
    float4x4 wvp : WorldViewProjection;

    float AmbientIntensity = 1;
    float4 AmbientColor : Ambient = float4(.5,.5,.5,1);

    float DiffuseIntensity = 1;
    float4 DiffuseColor : Diffuse = float4(1,1,1,1);

    float3 LightDirection : Direction = float3(1,1,0);

    We now need three new parameters in the shader, one for the specular color, one for its intensity and one for the camera position. We need the camera position as the effect is derived from the position of the viewer in contrast to both the ambient and the diffuse reflectance. So what the viewer sees is the degree of "shine" in relation to the angle of reflection from the surface, in the case of the Blinn-Phong method we use the "half" vector that lies between the direction of the viewer and the direction of the light.

    float4 SpecularColor : Specular = float4(1,1,1,1);
    float SpecularIntensity : Scalar = 1;

    float3 CameraPosition : CameraPosition;

    The only change we need to make to the structures is to add a CameraView member to hold the viewers direction relative to the object in the VS_OUT structure.

    struct VS_OUT
    {
        float4 Position : POSITION;
        float3 Light : TEXCOORD0;
        float3 Normal : TEXCOORD1;
        float3 CamView : TEXCOORD2;
    };

    Our vertex shader changes a bit too

    VS_OUT VS_AmbientDiffuse(VS_IN input)
    {
        VS_OUT output = (VS_OUT)0;
        output.Position = mul(input.Position,wvp);
        output.Normal = mul(input.Normal,world);
        output.Light = LightDirection;
        output.CamView = CameraPosition - mul(input.Position,world);
        return output;
    }

    As you can see, the transformation of the normal has changed, we are using the world parameter to transform the normal now and passing as is to the pixel shader, we do this so we can use it to transform the view vector there. This will also work on the previous shaders and is probably more efficient than passing a Inverse, Transpose, World matrix. Also, the light is passed strait through without normalization. This is because all normalization is going to be done in the pixel shader as the interpolation between the vertex and pixel shader do not always output vectors that are of unit length (almost quoted verbatim :p)

    As I am sure you have suspected we have also got to make changes to the pixel shader to calculate the specular reflection and apply this to our ambient and diffuse colour output.

    PS_OUT PS_AmbientDiffuse(VS_OUT input)
    {
        PS_OUT output = (PS_OUT)0;
        float3 Norm = normalize(input.Normal);
        float3 LightDir = normalize(input.Light);
        // Get ambient light
        AmbientColor *= AmbientIntensity;
        // Get diffuse light
        DiffuseColor = (DiffuseIntensity * DiffuseColor) * saturate(dot(LightDir,Norm));
        float3 Half = normalize(LightDir + normalize(input.CamView));   
        float specular = pow(saturate(dot(Norm,Half)),25);
        output.Color = AmbientColor + DiffuseColor + ((SpecularColor * SpecularIntensity) * specular);
        return output;
    }

    The ambient and diffuse calculations are the same as before but we have to do a bit more for the specular effect. First of all normalize the light and normal vectors and use these in the diffuse light calculation, then find the "half" vector so we can use it with the now normalized normal to get the specular effect. Then once all are calculated we put them all together and send them to the screen.

    Resulting in this effect

    In the accompanying solution you can move all three lights and alter there intensity, I have not added the ability to change specular values (running out of Xbox buttons), but I am sure you will be able to manage that if you implement it your self.

    You can download the solution for this post here.

    We have covered a fair bit of ground there, but I have enjoyed writing it as going back over it helps it solidify in my mind, so even if no one reads these posts, they are doing me the world of good :) Not sure what I will put in Episode 3 yet, textures and bump mapping I guess or even go over hemispheric lighting. As ever your C&C are welcome...

  • Basic HLSL Lighting Techniques - Episode 1: Ambient and Diffuse

    http://www.youtube.com/v/2n7_hXgoQdE <p><a href="http://www.youtube.com/v/2n7_hXgoQdE">http://www.youtube.com/v/2n7_hXgoQdE</a></p>

    All my HLSL and lighting knowledge has come pretty much from one book Programming Vertex and Pixel Shaders by Wolfgang Engel, which I insist you should buy if you are getting into shaders as it takes you from the very basics of where the Vertex and Pixel shaders fit into the flow of graphics pipeline as well as take you onto more advance techniques.

    What I intend to do here in this series is post my understanding of what this book has given me as well as my experience of playing with shaders in general.

    I will assume you know how to setup a vertex structure, load shaders and models up in XNA and how to pass parameters to shaders and will focus on just the HLSL code her. The downloadable samples will have full XNA 3.0 solutions in them showing how they are applied so if you don't know this; you can check out the code.


    Ambient Light

    We will start with the simplest form of lighting I know (though the hardest to explain :P), ambient light. Ambient light is light that has no real source, it's everywhere. I see it as the light generated by the environment around the object, so this light would be generated from say the sun, but it is being bounced off the objects around you, not direct sunlight. Ambient light has a colour and an intensity, but no direction.

    This is a very simple shader, we  have 3 parameters to pass to the shader, the world * view * projection matrix, a light intensity and a light colour.

     

    float4x4 wvp : WorldViewProjection;
    float AmbientIntensity = 1;
    float4 AmbientColor : AMBIENT = float4(.5,.5,.5,1);

    As you can see, I have also used semantics so FX Composer (I have version 1.8, my lappy wont run the new one) can interpret the parameters and given default values. I then define three structures, one for the data coming into the vertex shader, one for the data leaving the vertex shader (same structure used for data entering the pixel shader) and finally one for the output from the pixel shader.

    struct VS_IN
    {
        float4 Position : POSITION;
    };
    struct VS_OUT
    {
        float4 Position : POSITION;
    };
    struct PS_OUT
    {
        float4 Color : COLOR;
    };

    Simple stuff, all we need for this shader is the vertex position, and the color coming out of the pixel shader. The vertex shader sets up the return structure, takes the position data, transforms it and then passes it onto the pixel shader.

    VS_OUT VS_Ambient(VS_IN input)
    {
        VS_OUT output = (VS_OUT)0;
        output.Position = mul(input.Position,wvp);
        return output;
    }

    The pixel shader then sets up the output structure then populates the Color member with the AmbientIntensity with the AmbientColor, so lighting the model.

    PS_OUT PS_Ambient(VS_OUT input)
    {
        PS_OUT output = (PS_OUT)0;
        output.Color = AmbientIntensity * AmbientColor;
        return output;
    }

    In the shader you can see I am setting the ambient colour to a simple gray, in code I pass the shader parameter the color CornflowerBlue, the same as the clear colour, but I set the intensity to .5 giving the following image.

    So, as well as introduce the basic ambient lighting effect you also see how I have formed the shader. You don't have to have a VS_IN structure, all that data can be in the VS_Ambient method parameters and you don't have to have a PS_OUT structure you can just return a float4 and add the semantic COLOR to the end of the function header. I use these structures as it helps me structure my shader in a uniform manner.

    Diffuse Light

    Diffuse light; is light that has a direction and is a very simple lighting shader. In this sample I wont cover light intensity and colour, as we covered that in the Ambient Light shader. As we have a light direction we also need to know the normal of the surface the light will be bouncing off. The amount of light that is getting bounced off the surface will be proportional to the angle the light is hitting the face. So if the light is positioned directly over the surface, directly inline with the normal the diffuse light value will be 1, if it is directly opposite to the normal it will be 0.

    NOTE: In this shader we are passing the light DIRECTION, not the light POSITION. The light direction relative to the model can be obtained by subtracting the models position from the light position, e.g. LightDir = obj.Position - light.Position. You will see this in the download sample.

    We have two new parameters to add to the shader, a world; inverse; transpose matrix and a light direction.

    float4x4 itw : WorldInverseTranspose;
    float3 LightDirection : Direction = float3(0,50,10);

    In VS_IN we need to know the vertex normal so we add a normal to the structure. In VS_OUT we add two new members, one to hold the normalized light direction and one to hold the transformed normal, PS_OUT is unchanged.

    struct VS_IN
    {
        float4 Position : POSITION;
        float3 Normal : NORMAL;
    };

    struct VS_OUT
    {
        float4 Position : POSITION;
        float3 Light : TEXCOORD0;
        float3 Normal : TEXCOORD1;
    };

    struct PS_OUT
    {
        float4 Color : COLOR;
    };

    Now, as well as transforming the vertex position we need to transform the normal, this is done by multiplying it by the WorldInverseTranspose matrix. Transforming normals is different to transforming a position as they are just directions in space and all we need is to rotate them to get the correct direction for the transformed model, the data for this rotation is held in the WorldInverseTranspose which is then normalized.

    VS_OUT VS_Diffuse(VS_IN input)
    {
        VS_OUT output = (VS_OUT)0;
        output.Position = mul(input.Position,wvp);
        output.Light = normalize(LightDirection);
        output.Normal = normalize(mul(itw,input.Normal));
        return output;
    }

    We can now make our lighting calculation by finding the dot product of the light direction and the normal. The dot product gives the angle between two vectors. We  than use the saturate function to clamp the value between 0 and 1.

    PS_OUT PS_Diffuse(VS_OUT input)
    {
        PS_OUT output = (PS_OUT)0;
        output.Color = saturate(dot(input.Light,input.Normal));   
        return output;
    }

    The diffuse shader results in the following image

    And so combining these shaders gives us this result

    In the solution you can move around all three shaders at once, you can also move the light position around and see the models lit from different angles as can be seen in the clip above as well as alter the ambient intensity.

    As ever you C&C are welcome.

    You can download the solution to this sample here.