2

I just started messing around with shadow mapping. I understand the algorithm used. The thing is I cannot for the life of me figure out where I am messing up in the HLSL code. Here it is:

//These change
float4x4    worldViewProj;
float4x4    world;
texture     tex;

//These remain constant
float4x4    lightSpace;
float4x4    lightViewProj;
float4x4    textureBias;
texture     shadowMap;



sampler TexS = sampler_state
{
    Texture = <tex>;
    MinFilter = LINEAR;
    MagFilter = LINEAR;
    MipFilter = LINEAR;
    AddressU = WRAP;
    AddressV = WRAP;
};

sampler TexShadow = sampler_state
{
    Texture = <shadowMap>;
    MinFilter = LINEAR;
    MagFilter = LINEAR;
    MipFilter = LINEAR;
};

struct A2V
{   
    float3 posL : POSITION0;
    float2 texCo    : TEXCOORD0;
};

struct OutputVS
{
    float4 posH : POSITION0;
    float2 texCo    : TEXCOORD0;
    float4 posW : TEXCOORD2;
};

//Vertex Shader Depth Pass
OutputVS DepthVS(A2V IN)
{
    OutputVS OUT = (OutputVS)0;

    //Get screen coordinates in light space for texture map
    OUT.posH = mul(float4(IN.posL, 1.f), lightViewProj);
    //Get the depth by performing a perspective divide on the projected coordinates
    OUT.posW.x = OUT.posH.z/OUT.posH.w;
    return OUT;
}


//Pixel shader depth Pass
float4  DepthPS(OutputVS IN) : COLOR
{
    //Texture only uses red channel, just store it there
    return float4(IN.posW.x, 0, 0, 1);
}


//VertexShader Draw Pass
OutputVS DrawVS(A2V IN)
{
    OutputVS OUT = (OutputVS)0;

    //Get the screen coordinates for this pixel
    OUT.posH = mul(float4(IN.posL, 1.f), worldViewProj);
    //Send texture coordinates through
    OUT.texCo = IN.texCo;
    //Pass its world coordinates through
    OUT.posW = mul(float4(IN.posL, 1.f), world);
    return OUT;
}

//PixelShader Draw Pass
float4 DrawPS(OutputVS IN) : COLOR
{   
    //Get the pixels screen position in light space
    float4 texCoord = mul(IN.posW, lightViewProj);
    //Perform perspective divide to normalize coordinates [-1,1]
    //texCoord.x = texCoord.x/texCoord.w;
    //texCoord.y = texCoord.y/texCoord.w;
    //Multiply by texture bias to bring in range 0-1
    texCoord = mul(texCoord, textureBias);
    //Get corresponding depth value
    float prevDepth = tex2D(TexShadow, texCoord.xy);
    //Check if it is in shadow
    float4 posLight = mul(IN.posW, lightViewProj);
    float currDepth = posLight.z/posLight.w;
    if (currDepth >= prevDepth)
        return float4(0.f, 0.f, 0.f, 1.f);
    else
        return tex2D(TexS, IN.texCo);
}


//Effect info
technique ShadowMap
{
    pass p0
    {
        vertexShader = compile vs_2_0 DepthVS();
        pixelShader = compile ps_2_0 DepthPS();
    }

    pass p1
    {
        vertexShader = compile vs_2_0 DrawVS();
        pixelShader = compile ps_2_0 DrawPS();
    }
}

I have verified that all my matrices are correct and the depth map is being drawn correctly. Rewrote all of the C++ that handles this code and made it neater and I am still getting the same problem. I am not currently blending the shadows, just drawing them flat black until I can get them to draw correctly. The light uses an orthogonal projection because it is a directional light. I dont have enough reputation points to embed images but here are the URLs: Depth Map - https://i.stack.imgur.com/rNZV1.png Program output - https://i.stack.imgur.com/3AYmn.png

Any help or insight would be greatly appreciated as its for a school project. Thanks

user3355098
  • 173
  • 7
  • You can see that I have an actual shadow I just also have all that other goop as well and am not sure where that is coming from. – user3355098 Jun 11 '14 at 07:00

1 Answers1

2

The value you get from the depth buffer is float value that is from 0 to 1. As you probably already know, floating points are not accurate and the more decimal places you request the less accurate it is and this is where you end up with artifacts.

There are some things you can do. The easiest way is to make the value of the far and near Z in the projection matrix closer to each other so that the depth buffer will not use so many decimal places to represent how far away the object is. I usually find that having a value of 1-200 gives me a fairly good accurate result.

Another easy thing you can do is increase the size of the texture you are drawing on as that will give you more pixels and therefore it will represent the scene more accurately.

There are also a lot of complex things that games engines can do to improve on shadow mapping artifacts but you can write a book about that and if you really do want to get into it than I would recommended you start with the blog.

Caesar
  • 9,483
  • 8
  • 40
  • 66
  • So you think it is more of a precision issue and not something else I am missing? I tend to think the same only because, after a month on this problem, I understand the algorithm pretty well and it makes sense...intuitively. I am using near and far clipping planes of 1.f and 100.f, and I also tried adding a simple bias using something along the lines of(and I tried many biases): if (abs(curDepth - prevDepth) < .001) but those tended to black out a majority of the scene. I increased the texture size from the 512 I was using to a 1024 and it gave me the same basic result with smoother edges. – user3355098 Jun 11 '14 at 09:12
  • @user3355098 There is nothing I can guarantee unless I spend a good amount of time looking at your code, but this is a very frequent issue and the solutions I gave tend to solve it. DirectX provides code of shadow mapping so you might want to compare that to your code. – Caesar Jun 11 '14 at 09:53
  • Yeah, I have looked at many different shadow mapping tutorials, those included. Ended up watching an hour long video on youtube.com. As far as I can tell everything is correct. I think some of the artifacts stem from the fact that certain vectors are tangent with the projection angle. That will just happen with a sphere though. Still doesn't explain why the entire corner is black. – user3355098 Jun 11 '14 at 11:26
  • @user3355098 The corner is black is probably because your sampling outside the textures boundaries. – Caesar Jun 11 '14 at 12:06
  • I added this to it to make sure I wasn't indexing outside the texture boundaries and got the same result: if (saturate(texCoord.x) == texCoord.x && saturate(texCoord.y) == texCoord.y) { if (curDepth > prevDepth) return float4(0.f, 0.f, 0.f, 1.f); } return tex2D(TexS, IN.texCo); – user3355098 Jun 11 '14 at 19:31
  • @user3355098 Try only checking if the texCoord is within range of 0 to 1 otherwise assume it is not in shadow. if ( texCoord .x < 1.0f && texCoord .x > 0.0f && texCoord .y < 1.0f && texCoord .y > 0.0f) – Caesar Jun 12 '14 at 04:04
  • Using this and some PCF and blending is giving me way better results. Still some small artifacts but they are barely noticeable. Thanks for the help! – user3355098 Jun 12 '14 at 22:49