XNA for C#
DirectX 9 for C#
DirectX 9 for C++
DirectX 9 for VB
My XNA Book
       Go to section on this site

Additional info

Latest Forum posts

 Account settings
  Posted by: Anonymous
  When: 07/05/2014 at 09:48:39

 forced subtitle
  Posted by: Applefly
  When: 07/05/2014 at 06:00:48

 convert DVD into PMS
  Posted by: Applefly
  When: 07/05/2014 at 05:55:25

 DVD to Digital Copy easily
  Posted by: VIKIVannessa
  When: 05/05/2014 at 06:52:29

 DVD on Xbox 360/Xbox One Console
  Posted by: VIKIVannessa
  When: 05/05/2014 at 06:51:47

 Extract .Srt Subtitles
  Posted by: Applefly
  When: 04/05/2014 at 03:54:38

 Encode Movie collection
  Posted by: Applefly
  When: 04/05/2014 at 03:52:41

 Convert DVD to WMV
  Posted by: Applefly
  When: 29/04/2014 at 05:53:50

 rip DVDs into digital files
  Posted by: Applefly
  When: 29/04/2014 at 05:51:20

 iTunes movies/music to Kindle Fire
  Posted by: ciciyu80
  When: 29/04/2014 at 05:10:20


Setting individual Z-values

Last chapter we have calculated the shadow map for 3 lights, and stored them together in 1 texture, where each light corresponds to a separate color. This would mean every pixel of the shadow map should be a combination of the 3 colors. When you have a closer look at the shadow map, youíll see this isnít the case.

So what happened? Take a look at the image below. We will render the view of the 3 lights to 1 single render target, the texture. This means they all share the same Z buffer. Every pixel being drawn will set a value between 0 and 1 into the Z buffer for that pixel. Because of this, the Z buffer will already be completely filled with values after the first light has finished drawing its shadow map.

Every pixel as seen by the second light will be tested against this non-empty Z buffer. As a result, every pixel that is further away from the second light than it was from the first light, will have a greater Z value than the one present in the Z buffer, and will not be drawn!

The same holds for the third light: only the pixels, for which the distance to light 3 is smaller than the distance was to light 1 AND light 2, will be drawn to the shadow map. This way, a lot of pixels of light 2 and 3 will not pass the Z test, and thus will not be drawn to our Shadow Map. So how are we supposed to solve this problem?

Although Iím aware more scalable solutions exist, I prefer the solution below as it shows you something new.

Have a look at the image below. We are going to split up our depth buffer. We will put all Z values of the first light in the region [0.66 1], all Z values for light 2 in the region [0.33 0.66] and all Z values of the third light in region [0 0.33].

This way, light 1 will first fill the upper region of the z buffer. Next, the pixels of the second light will be tested against the non-empty Z buffer. But because all values present in the Z buffer are greater than the new values of light 2, all pixels will pass the Z test and will be drawn!

The same for light 3: every pixel of light 3 will be set to the smalles Z values, and will thus be drawn.

1 note on this:
To simplify things a bit, I said all pixels as seen by all lights would be drawn. This still implicates that some pixels of some triangles will not be drawn, as light 2 will try to draw pixels of the street that are behind the lampposts. This is the normal intention of the Z buffer, so itís a good thing these pixels arenít drawn.

So how can we translate all this into code? Itís pretty easy, actually. Remember a pixel shader must set the color of every pixel, but it can also set the depth value. Go to your HLSL code, and find the struct defining the output of our first pixel shader, and add this simple line:

float Depth     : DEPTH;

Which indicates weíll have to set the depth value in our pixel shader. In the image above, you can see how we must map our depth values, which is done by the next line of code, which you can put in your pixel shader:

Output.Depth = Output.Color[CurrentLight]/3.0f + (2-CurrentLight)*0.33f;

First we divide the normal depth value by 3, and then we add the offset. Thatís it, so when you run the code you should see a screen like this:

DirectX Tutorial 18 - Adjusting Z values

If you appreciate the amount of time I spend creating and updating
these pages, feel free to donate -- any amount is welcome !

Click here to go to the forum on this chapter!

Or click on one of the topics on this chapter to go there:
  • Useable to boost CSM?
          I was wondering if using an individual zbuffer mig...
  • Antialiased Shadow
          Heey Reimer, Is it possible to add antialiasing...
  • Why?
          I am not sure I understand this tutorial well. Why...

    Compare this shadow map against the shadow map we saw last chapter. You should notice that every pixel contains some color information about each light. In the red channel, you can identify the car and the lampposts.

    Now our ShadowedScene technique can sample the correct depth values from the shadow map, which enables it to draw the scene correctly. In the blue and green color channels, you can see the circles (which are caused by the lamppost itself), the border of the pavement and the transition of the pavement to the wall.

    If you take a good look, you can see the car is being cut out in the green channel, which causes the shadow be beneath the car in our colored scene.

    This concludes the shadow mapping algorithm for 3 lights, the last chapter will cover some post-processing enhancements.

    The HLSL code:

    struct SMapVertexToPixel
        float4 Position     : POSITION;    
        float3 Position2D    : TEXCOORD0;

    struct SMapPixelToFrame
        float4 Color : COLOR0;

         float Depth     : DEPTH;


    struct SSceneVertexToPixel
        float4 Position             : POSITION;
        float4 ShadowMapSamplingPos : TEXCOORD0;    
        float4 RealDistance            : TEXCOORD1;
        float2 TexCoords            : TEXCOORD2;
        float3 Normal                : TEXCOORD3;
        float3 Position3D            : TEXCOORD4;

    struct SScenePixelToFrame
        float4 Color : COLOR0;

    //------- Constants --------
    float4x4 xCameraViewProjection;
    float4x4 xLightViewProjection[3];
    float4x4 xRotate;
    float4x4 xTranslateAndScale;

    float4 xLightPos[3];
    float xLightPower[3];
    float xMaxDepth;

    //------- Texture Samplers --------

    Texture xColoredTexture;

    sampler ColoredTextureSampler = sampler_state { texture = <xColoredTexture> ; magfilter = LINEAR; minfilter = LINEAR; mipfilter=LINEAR; AddressU = mirror; AddressV = mirror;};
    Texture xShadowMap;

    sampler ShadowMapSampler = sampler_state { texture = <xShadowMap> ; magfilter = LINEAR; minfilter = LINEAR; mipfilter=LINEAR; AddressU = clamp; AddressV = clamp;};
    Texture xCarLightTexture;

    sampler CarLightSampler = sampler_state { texture = <xCarLightTexture> ; magfilter = LINEAR; minfilter=LINEAR; mipfilter = LINEAR; AddressU = clamp; AddressV = clamp;};    
    Texture xSpotLightTexture;

    sampler SpotLightSampler = sampler_state { texture = <xSpotLightTexture> ; magfilter = LINEAR; minfilter=LINEAR; mipfilter = LINEAR; AddressU = clamp; AddressV = clamp;};    
    //------- Vertex Shaders --------

    SMapVertexToPixel ShadowMapVertexShader( float4 inPos : POSITION, uniform int CurrentLight)
        SMapVertexToPixel Output = (SMapVertexToPixel)0;
        float4x4 preWorld = mul(xRotate, xTranslateAndScale);
        float4x4 preLightWorldViewProjection = mul (preWorld, xLightViewProjection[CurrentLight]);
        Output.Position = mul(inPos, preLightWorldViewProjection);
        Output.Position2D = Output.Position;
        return Output;    

    SSceneVertexToPixel ShadowedSceneVertexShader( float4 inPos : POSITION, float2 inTexCoords : TEXCOORD0, float3 inNormal : NORMAL, uniform int CurrentLight)
        SSceneVertexToPixel Output = (SSceneVertexToPixel)0;
        float4x4 preWorld = mul(xRotate, xTranslateAndScale);
        float4x4 preCameraWorldViewProjection = mul (preWorld, xCameraViewProjection);
        float4x4 preLightWorldViewProjection = mul (preWorld, xLightViewProjection[CurrentLight]);
        Output.Position = mul(inPos, preCameraWorldViewProjection);
        Output.ShadowMapSamplingPos = mul(inPos, preLightWorldViewProjection);
        Output.RealDistance = Output.ShadowMapSamplingPos.z/xMaxDepth;
        Output.TexCoords = inTexCoords;
        Output.Normal = mul(inNormal, xRotate);
        Output.Position3D = inPos;
        return Output;    

    //------- Pixel Shaders --------

    float DotProduct(float4 LightPos, float3 Pos3D, float3 Normal)
        float3 LightDir = normalize(LightPos - Pos3D);
        return dot(LightDir, Normal);

    SMapPixelToFrame ShadowMapPixelShader(SMapVertexToPixel PSIn, uniform int CurrentLight)
        SMapPixelToFrame Output = (SMapPixelToFrame)0;

        Output.Color[CurrentLight] = PSIn.Position2D.z/xMaxDepth;

         Output.Depth = Output.Color[CurrentLight]/3.0f + (2-CurrentLight)*0.33f;

        return Output;

    SScenePixelToFrame ShadowedScenePixelShader(SSceneVertexToPixel PSIn, uniform int CurrentLight)
        SScenePixelToFrame Output = (SScenePixelToFrame)0;

        float2 ProjectedTexCoords;
        ProjectedTexCoords[0] = PSIn.ShadowMapSamplingPos.x/PSIn.ShadowMapSamplingPos.w/2.0f +0.5f;
        ProjectedTexCoords[1] = -PSIn.ShadowMapSamplingPos.y/PSIn.ShadowMapSamplingPos.w/2.0f +0.5f;

        if ((saturate(ProjectedTexCoords.x) == ProjectedTexCoords.x) && (saturate(ProjectedTexCoords.y) == ProjectedTexCoords.y))
            float StoredDepthInShadowMap = tex2D(ShadowMapSampler, ProjectedTexCoords)[CurrentLight];    
            if ((PSIn.RealDistance.x - 1.0f/100.0f) <= StoredDepthInShadowMap)    
                float LightTextureFactor;
                if (CurrentLight == 0)
                    LightTextureFactor = tex2D(CarLightSampler, ProjectedTexCoords).r;
                    LightTextureFactor = tex2D(SpotLightSampler, ProjectedTexCoords).r;
                float DiffuseLightingFactor = DotProduct(xLightPos[CurrentLight], PSIn.Position3D, PSIn.Normal);
                float4 ColorComponent = tex2D(ColoredTextureSampler, PSIn.TexCoords);
                Output.Color = ColorComponent*LightTextureFactor*DiffuseLightingFactor*xLightPower[CurrentLight];

        return Output;

    //------- Techniques --------

    technique ShadowMap
        pass Pass0
            cullmode = ccw;
            colorwriteenable = red;    
            VertexShader = compile vs_2_0 ShadowMapVertexShader(0);
            PixelShader = compile ps_2_0 ShadowMapPixelShader(0);
        pass Pass1
            colorwriteenable = green;
            VertexShader = compile vs_2_0 ShadowMapVertexShader(1);
            PixelShader = compile ps_2_0 ShadowMapPixelShader(1);
        pass Pass2
            colorwriteenable = blue;
            VertexShader = compile vs_2_0 ShadowMapVertexShader(2);
            PixelShader = compile ps_2_0 ShadowMapPixelShader(2);

    technique ShadowedScene
        pass Pass0
            VertexShader = compile vs_2_0 ShadowedSceneVertexShader(0);
            PixelShader = compile ps_2_0 ShadowedScenePixelShader(0);
        pass Pass1
             SRCBLEND = ONE;
            DESTBLEND = ONE;
            ALPHABLENDENABLE = true;
            VertexShader = compile vs_2_0 ShadowedSceneVertexShader(1);
            PixelShader = compile ps_2_0 ShadowedScenePixelShader(1);
        pass Pass2
            VertexShader = compile vs_2_0 ShadowedSceneVertexShader(2);
            PixelShader = compile ps_2_0 ShadowedScenePixelShader(2);


    If you appreciate the amount of time I spend creating and updating
    these pages, feel free to donate -- any amount is welcome !

    - Website design & XNA + DirectX code : Riemer Grootjans -
    ©2003 - 2011 Riemer Grootjans
  • Translations

    This site in English
    This site in Korean
    This site in Czech

    Microsoft MVP Award

    2007 - 2011 MVP Award
    DirectX - XNA


    XNA 2.0 Recipes Book (8)
    XNA 3.0 Recipes Book (8)
    Extra Reading (3)
    Matrices: geometrical
    Matrix Mathematics
    Homogenous matrices
    Community Projects (1)
    Tutorials (160)
    XNA 4.0 using C# (89)
    DirectX using C# (54)
    Series 1:Terrain (14)
    Series 2: Flightsim (19)
    Series 3: HLSL (19)
    Starting point
    HLSL Introduction
    Vertex Shader
    Shaded triangle
    Pixel Shader
    Textured Triangle
    Triangle Strip
    World transform
    Adding normals
    The first light
    Shadow mapping
    Render To Texture
    Projective texturing
    The first shadow
    Shaping the light
    Multiple lights
    Adjusting Z values
    Finishing touch
    Short Tuts (2)
    DirectX using C++ (15)
    DirectX using VB (2)
    -- Expand all --

    Thank you!

    Support this site --
    any amount is welcome !

    Stay up-to-date

    I don't have the time to keep a News section, so stay informed about the updates by clicking on this RSS file!