Xbox LIVE Indie Games
Sort Discussions: Previous Discussion Next Discussion
Page 1 of 1 (15 posts)

Shadow Mapping Problems

Last post 11/17/2011 7:25 PM by CC Ricers. 14 replies.
  • 11/15/2011 10:47 AM

    Shadow Mapping Problems

    I'm trying to implement shadow mapping. The two problems I have: (1) the shadows are not 'attached' to the objects that cast them and (2) the shadows move when camera moves. Moreover, they don't move the same speed as the camera (the shadows 'move' faster). Does anyone have any idea why this could be happening?

    HLSL Code:
    // common parameters  
    float4x4 World;  
    float4x4 View;  
    float4x4 Projection;  
    float AmbientIntensity = 1.0f;  
    float4x4 LightViewProj;  
    texture ShadowMapTexture;  
    sampler ShadowMapTextureSampler = sampler_state  
    {  
        texture = <ShadowMapTexture>;  
        MagFilter = POINT;  
        MinFilter = POINT;  
        MipFilter = POINT;  
    };  
    float DepthBias = 0.001f;  
     
    // parameters used with the NoTexture technique  
    float Alpha = 1.0f;  
    float3 LightDirection = float3(0.9428091, 0.2357023, -0.2357023);  
    float3 DiffuseColor;  
     
    // parameters used with the Map technique  
    texture Texture;  
    sampler TextureSampler = sampler_state  
    {  
        texture = <Texture>;  
    };  
     
    struct NoTextureVSInput  
    {  
        float4 Position : POSITION0;  
        float3 Normal : NORMAL0;  
    };  
     
    struct NoTextureVSOutput  
    {  
        float4 Position : POSITION0;  
        float3 Normal : TEXCOORD0;  
        float4 WorldPosition : TEXCOORD1;  
    };  
     
    struct MapVSInput  
    {  
        float4 Position : POSITION0;  
        float2 TexCoord : TEXCOORD0;  
    };  
     
    struct MapVSOutput  
    {  
        float4 Position : POSITION0;  
        float2 TexCoord : TEXCOORD0;  
        float4 WorldPosition : TEXCOORD1;  
    };  
     
    struct CreateShadowMapVSOutput  
    {  
        float4 Position : POSITION0;  
        float Depth : TEXCOORD0;  
    };  
     
    NoTextureVSOutput NoTextureVS(NoTextureVSInput input)  
    {  
        NoTextureVSOutput output;  
        float4 worldPosition = mul(input.Position, World);  
        float4 viewPosition = mul(worldPosition, View);  
        output.Position = mul(viewPosition, Projection);  
        output.Normal = mul(input.Normal, World);  
        output.WorldPosition = worldPosition;  
        return output;  
    }  
     
    float4 NoTexturePS(NoTextureVSOutput input) : COLOR0  
    {  
        float intensity = saturate(dot(input.Normal, LightDirection) + AmbientIntensity);  
        float3 color = intensity * DiffuseColor;  
        float4 lightingPosition = mul(input.WorldPosition, LightViewProj);  
        float2 shadowTexCoord = 0.5 * lightingPosition.xy /   
            lightingPosition.w + float2(0.5, 0.5);  
        shadowTexCoord.y = 1.0f - shadowTexCoord.y;  
        float shadowdepth = tex2D(ShadowMapTextureSampler, shadowTexCoord).r;      
        float ourdepth = (lightingPosition.z / lightingPosition.w) - DepthBias;  
        if (shadowdepth < ourdepth)  
            color *= float3(0.5, 0.5, 0.5);  
        return float4(color, Alpha);  
    }  
     
    MapVSOutput MapVS(MapVSInput input)  
    {  
        MapVSOutput output;  
        float4 worldPosition = mul(input.Position, World);  
        float4 viewPosition = mul(worldPosition, View);  
        output.Position = mul(viewPosition, Projection);  
        output.TexCoord = input.TexCoord;  
        output.WorldPosition = worldPosition;  
        return output;  
    }  
     
    float4 MapPS(MapVSOutput input) : COLOR0  
    {  
        float3 color = tex2D(TextureSampler, input.TexCoord);  
        color.rgb *= AmbientIntensity;  
        float4 lightingPosition = mul(input.WorldPosition, LightViewProj);  
        float2 shadowTexCoord = 0.5 * lightingPosition.xy /  
            lightingPosition.w + float2(0.5, 0.5);  
        shadowTexCoord.y = 1.0f - shadowTexCoord.y;  
        float shadowdepth = tex2D(ShadowMapTextureSampler, shadowTexCoord).r;      
        float ourdepth = (lightingPosition.z / lightingPosition.w) - DepthBias;  
        if (shadowdepth < ourdepth)  
            color *= float3(0.5, 0.5, 0.5);  
        return float4(color, Alpha);  
    }  
     
    CreateShadowMapVSOutput CreateShadowMapVS(float4 Position: POSITION)  
    {  
        CreateShadowMapVSOutput output;  
        output.Position = mul(Position, mul(World, LightViewProj));   
        output.Depth = output.Position.z / output.Position.w;      
        return output;  
    }  
     
    float4 CreateShadowMapPS(CreateShadowMapVSOutput input) : COLOR  
    {  
        return float4(input.Depth, 0, 0, 0);  
    }  
     
    technique NoTexture  
    {  
        pass Pass1  
        {  
            VertexShader = compile vs_1_1 NoTextureVS();  
            PixelShader = compile ps_2_0 NoTexturePS();  
        }  
    }  
     
    technique Map  
    {  
        pass Pass1  
        {  
            VertexShader = compile vs_1_1 MapVS();  
            PixelShader = compile ps_2_0 MapPS();  
        }  
    }  
     
    technique CreateShadowMap  
    {  
        pass Pass1  
        {  
            VertexShader = compile vs_1_1 CreateShadowMapVS();  
            PixelShader = compile ps_2_0 CreateShadowMapPS();  
        }  
    }  
     
  • 11/15/2011 2:40 PM In reply to

    Re: Shadow Mapping Problems

    When a shadow is not attached, this is called Peter Panning and it is fixed by messing with your DepthBias.

    As far as them moving too quickly, either your LightProject matrix is changes and it shouldn't or you are sampling the depth buffer incorrectly.  User PIX to debug this.  Here is how:

    1.  Install PIX.
    2.  New Experiment in single capture mode
    3.  Press F12 in your game when you are at a good example of things being broke.
    4.  Close your game and pix will pop up.
    5.  First at the top, click on the block that represents the frame you captured.
    6.  On the left side, again, click the frame you captured.  It should say "Frame: 123123" or something.
    7.  In the second window down, filter all that mess with Usage: Texture and Type: RenderTarget (all this is off the top of my head, so you may have to look around).
    8.  Double click on the render targets until you find your shadowmap.  Make sure it looks right.
    9.  Click on the Render tab and see your scene.
    10. Right click on that scene on a shadow thats in a place where it shouldn't be and select Debug This Pixel.
    11. This will list all the pixel shader calls that made up that pixel.  There should only be a few.
    12. Scroll down the list and pick one by clicking on "Debug Pixel Shader" which will be a hyperlink in that list.
    13. Now you can step through your pixel shader just like you can code in Visual studios.  You can see all the values of variables and what it does at each step.

    If you are going to program shaders, it is CRITICAL that you learn how to do this.  It only takes a second once you have the hang of it.
  • 11/15/2011 6:09 PM In reply to

    Re: Shadow Mapping Problems

    Thank you very much for your response.

    It seems I was so happy to see some shadows that I forgot to have my models rendered with the DrawShadowMap technique. I did that and now I see no shadows at all. I can see in PIX that lightingPosition equals to (0, 0, 0, 0), which means that either input.WorldPosition or LightViewProj must be zero as well, but PIX won't show me these values.

    As for the shadow map render target - I did't find it in PIX, but when I saved the texture as JPG, it was empty (well, not entirely empty - the objects that are not rendered with the CreateShadowMap technique can be seen there and they cast the unattached moving shadows).

    One more thing - is it alright to call base.Draw twice? My objects are DrawableGameComponents, so calling base.Draw twice should theoretically render them properly:
    protected override void Draw(GameTime gameTime)  
    {  
        Globals.DrawingShadowMap = true;  
        GraphicsDevice.SetRenderTarget(0, Globals.ShadowMapRenderTarget);  
        GraphicsDevice.DepthStencilBuffer = shadowMapDepthStencilBuffer;  
        GraphicsDevice.Clear(Color.White);  
        base.Draw(gameTime);  
        Globals.DrawingShadowMap = false;  
        GraphicsDevice.SetRenderTarget(0, null);  
        GraphicsDevice.DepthStencilBuffer = originalDepthStencilBuffer;  
        GraphicsDevice.Clear(Color.CornflowerBlue);  
        base.Draw(gameTime);  
        ...  
    }
     
  • 11/15/2011 7:06 PM In reply to

    Re: Shadow Mapping Problems

    Sounds like your lightViewProjection is incorrect.  How do you create it?  Spend some time on that.
    Also, you are using XNA 3.1.  Probably should upgrade.  Education section shows you how to make a LightViewProjection matrix for directional lights.  Focus on making your shadowmap look correct first.  You don't have to save it as a jpeg, you can draw it too the screen using spritebatch after you make it.
  • 11/15/2011 7:20 PM In reply to

    Re: Shadow Mapping Problems

    publisher5:

    It seems I was so happy to see some shadows that I forgot to have my models rendered with the DrawShadowMap technique. I did that and now I see no shadows at all. I can see in PIX that lightingPosition equals to (0, 0, 0, 0), which means that either input.WorldPosition or LightViewProj must be zero as well, but PIX won't show me these values.

    As for the shadow map render target - I did't find it in PIX, but when I saved the texture as JPG, it was empty (well, not entirely empty - the objects that are not rendered with the CreateShadowMap technique can be seen there and they cast the unattached moving shadows).

    PIX will definitely show you input.WorldPosition. You should be able to see it in the debugger, and also in the mesh tab when you have a draw call highlighted (you can see what was output from the vertex shader for every vertex).

    As for the shadow map render target, you should be able to see it if you click on the render tab, and then step through the draw calls that are rendering to the shadow map render target (use the 'D' buttons to jump to the next draw call).

    I've started a series of video PIX tutorials. Well, only one so far, and it is here. I'm not sure if it would be useful for you, the first one is just kind of a broad overview.

    I'm still trying to figure out what to go into for subsequent tutorials. Real world problems like yours would be best... if you want to send me your project I'd be glad to take a look at it in PIX, and then I could feature it in a tutorial :-)


  • 11/15/2011 9:39 PM In reply to

    Re: Shadow Mapping Problems

    Broke Down Games:
    Sounds like your lightViewProjection is incorrect.  How do you create it?  Spend some time on that.

    Also, you are using XNA 3.1.  Probably should upgrade.  Education section shows you how to make a LightViewProjection matrix for directional lights.  Focus on making your shadowmap look correct first.  You don't have to save it as a jpeg, you can draw it too the screen using spritebatch after you make it.


    I just assign it the view and projection matrices of my initial camera position. I think the shadow map looks correct - it should look like my scene at the start of the game (except for the colors of course). I tried to port it to XNA 4.0, but I was unable to make my morphing facial animations work.

    Phil Fortier:
    PIX will definitely show you input.WorldPosition. You should be able to see it in the debugger, and also in the mesh tab when you have a draw call highlighted (you can see what was output from the vertex shader for every vertex).


    Perhaps I cannot see it because I'm using an old version of PIX.

    Phil Fortier:
    Real world problems like yours would be best... if you want to send me your project I'd be glad to take a look at it in PIX, and then I could feature it in a tutorial :-)

    Wow, I would be honored :-). You can download it here: CrazyLand.zip. I'm sorry for the code - it's kinda messy... There are some TODOs with temporary code. In this version, only the player tokens cast shadows (they are rendered with a different effect). If you want to see what I saw in the first place, you should remove the lines where I assign the technique, that is, line 29 in Cavern.cs, line 27 in Colony.cs and line 73 in Obstacle.cs.
  • 11/15/2011 10:01 PM In reply to

    Re: Shadow Mapping Problems

    You shadow maps should be the view of your scene from the eyes of the light, not of the camera.  You create a view and projection matrix by treating your light as a camera.  If it is a directional light, you use an ortho matrix which makes things look flat.  The education section has an example.  Copying your camera will be problematic as the far plane is probably much too far for a shadow maps needed precision.
  • 11/16/2011 12:13 AM In reply to

    Re: Shadow Mapping Problems

    Yeah, well, related to what the previous poster said, it looks like the View*Proj matrices you draw the shadow map with (using MorphSkinnedShader.fx) are different than the LightViewProj matrix you pass in to the shader that draws the ground reads from the shadow map (the "Map" technique in ShadowMap.fx).

    So basically, you need to ensure LightViewProj matches the View * Projection matrix that you use when rendering objects into the shadow map.

    Err... it also looks like you're using the wrong shader to draw the shadow map. You're rendering a color with it, you should be rendering the depth.
  • 11/16/2011 2:37 AM In reply to

    Re: Shadow Mapping Problems

    I should add that I saw a lot less information than usual in PIX's shader debugger when debugging your project. I noticed you're using XNA 3.0. I wonder if the shader compiler that comes with XNA 4.0 adds more information to the debug info for the shader?

    Like you said, I wasn't able to see shader input variables - normally I'm able to when debugging XNA 4.0 projects.

  • 11/16/2011 4:45 PM In reply to

    Re: Shadow Mapping Problems

    Phil Fortier:
    So basically, you need to ensure LightViewProj matches the View * Projection matrix that you use when rendering objects into the shadow map.

    What do you mean by matching them? LightViewProj should NOT be equal to View * Projection, should it?

    Phil Fortier:
    Err... it also looks like you're using the wrong shader to draw the shadow map. You're rendering a color with it, you should be rendering the depth.

    So should I rewrite my CreateShadowMapPS Pixel Shader like this?:
    struct CreateShadowMapPSOutput  
    {  
        float4 Color : COLOR;  
        float Depth : DEPTH;  
    };  
     
    CreateShadowMapPSOutput CreateShadowMapPS(CreateShadowMapVSOutput input)  
    {  
        //return float4(input.Depth, 0, 0, 0);  
        CreateShadowMapPSOutput output;  
        output.Color = float4(input.Depth, 0, 0, 0);  
        output.Depth = input.Depth;  
        return output;  
    I tried that, but to no effect. The shader should be alright - I took it directly from the Shadow Mapping sample.
  • 11/16/2011 5:19 PM In reply to

    Re: Shadow Mapping Problems

    publisher5:
    Phil Fortier:
    Err... it also looks like you're using the wrong shader to draw the shadow map. You're rendering a color with it, you should be rendering the depth.

    So should I rewrite my CreateShadowMapPS Pixel Shader like this?:


    No, your original pixel shader was just fine.  Pixel shaders have to return the COLOR semantic, but you don't normally want them returning the DEPTH semantic.  You usually want to be rendering your shadow depths to a 32-bit Single buffer, not a 4x8 bit Color one, but that's controlled by which rendertarget you have bound, not by your pixel shader's output setup.  For your purposes, this would work just fine:

    float4 CreateShadowMapPS( CreateShadowMapVSOutput input ) : COLOR0
    {
        return input.Depth;
    }
  • 11/16/2011 7:23 PM In reply to

    Re: Shadow Mapping Problems

    When I debugged it, the player token was being rendered into the shadow map using the following shader in MorphSkinnedModel.fx:

    // Pixel shader program. 
    float4 PixelShader(PS_INPUT input) : COLOR0 
        float4 color = float4(0, 0, 0, 1); 
     
        color.rgb += Color; 
        color.rgb *= input.Lighting; 
         
        return color; 


    Instead, it should be a shader that is putting depth into the r channel of the output color (since you're using SurfaceFormat.Single I believe, correct?).

    Fundamentally, you have a shader that you're using to render skinned models, but it has no pixel shader intended to draw to shadow maps.

    And yes, the View * Proj used when you render an object into the shadow map (which is done from the light's point of view) should be the same as the LightViewProj you use when you check against  shadow map while rendering an object from the camera's point of view.




  • 11/17/2011 5:15 PM In reply to

    Re: Shadow Mapping Problems

    Oh, you were talking about MorphSkinnedShader.fx - I didn't realize that, sorry. Well, that shader does not yet implement shadow mapping; ShadowMap.fx is the shader relevant to our discussion. I don't understand why the player tokens do cast shadows (they shouldn't) and the other objects don't (they should). As I said before, if you remove the lines I wrote of earlier, the other objects will cast shadows, too. And they too behave strangely.

    After the line
    float shadowdepth = tex2D(ShadowMapTextureSampler, shadowTexCoord).r; 
    shadowdepth equals 0.000, but I don't understand how this can be possible, since I clear the render target to white color, which means that sampling it at any coordinates should return all 1's. I don't know, I must be missing something obvious...

    Phil Fortier:
    ...(since you're using SurfaceFormat.Single I believe, correct?).

    Correct.
  • 11/17/2011 7:08 PM In reply to

    Re: Shadow Mapping Problems

    Ok, I tried commenting out the lines you mentioned. And indeed, the objects all cast shadows now, but they are in the wrong spot.

    It looks like the same problem: the (View * Projection) matrix used when rendering the shadow map is different than the LightViewProj matrix used when drawing the actual object (e.g. the green terrain is what I was looking at).

    Also, I wasn't seeing shadowdepth be 0. It was 1 where I debugging a terrain pixel with no shadow (what I would expect) and something like 0.7 where I debugged a terrain pixel with a shadow (seems reasonable).
  • 11/17/2011 7:25 PM In reply to

    Re: Shadow Mapping Problems

    Also, just to add to what was previously said, I may suggest to move the LightViewProj matrix multiplication to the vertex shader of your shadow mapping techniques. input.WorldPosition serves no other purpose in the pixel shader, so it would be fine to move it to MapVS and NoTextureVS and change output.WorldPosition = worldPosition; to output.LightingPosition = mul(worldPosition, LightViewProj);  This is how I have it set up in my shadow mapping shader. It produces basically the same output for shadow mapping and probably better to compute it on a per-vertex level than a per-pixel level, since the pixel shader interpolates between the values. I renamed the structure output member to LightingPosition so that it's made more clear what it is and where it would fit in the pixel shader.
Page 1 of 1 (15 posts) Previous Discussion Next Discussion