# Deferred Shadow Maps [Fixed]

This topic is 2189 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

## Recommended Posts

Hi folks,

I'm running in to trouble trying to get my deferred light maps working for directional lights.

In my light map pass I render all of the geometry in the scene using a world/view/projection matrix based on the position and direction of the light source. The result I'm getting looks like what I'd expect it to be.

Trouble comes in (I'm assuming) the pixel shader when I'm try to convert the current world position in to light-screen-space. I'd really appreciate it if you could run your eyes over the logic...

So the setup for the light matrix that I pass in to the pixel shader's constant buffer :

// Calculate the world/view/projection matrix for the light
XMMATRIX world	= XMLoadFloat4x4( &aCamera->GetWorld() );
XMMATRIX view	= XMMatrixLookAtLH( XMLoadFloat3(&aLight->GetPosition()), XMLoadFloat3(&lightDirection), XMLoadFloat3(&XMFLOAT3(0.0f, 1.0f, 0.0f)) );
XMMATRIX projection = XMMatrixPerspectiveFovLH( XMConvertToRadians(30.0f), 1.0f, 1.0f, 1024.0f );
XMMATRIX lightWVP   = XMMatrixMultiply( XMMatrixMultiply(world, view), projection );

// Bind the matrix to the lighting buffer
lightingBuffer->myLightViewProjection = XMMatrixTranspose( lightWVP );


Then in the shader I calculate the world space position (currentDepth is sampled from the depth g-buffer):

// Calculate the screen-space position
float4 currentPosition;
currentPosition.x = anInput.myTexCoords.x * 2.0f - 1.0f;
currentPosition.y = -(anInput.myTexCoords.y * 2.0f - 1.0f);
currentPosition.z = currentDepth;
currentPosition.w = 1.0f;

// Transform the screen space position in to world-space
currentPosition = mul( currentPosition, myInverseViewProjection );
currentPosition /= currentPosition.w;


And then I convert the current position in to light-space :

// multiply current position by light view projection to get light-screen space
float4 lightScreenPosition = mul( currentPosition, myLightViewProjection );

// convert position in to texture coordinates
float2 lightTextureCoords = 0.5f * (float2(lightScreenPosition.x, -lightScreenPosition.y) + 1);

// sample from light depth map
float lightDepth = ShadowMap.Sample( ShadowSampler, lightTextureCoords ).r;


And I guess if the lightDepth > currentDepth then I should be returning my ambient light for the current pixel?

if( lightDepth > currentDepth )
{
return float4( 0.1f, 0.1f, 0.1f, 0.1f );
}

// otherwise continue on with the light map calculations


I'm obviously messing something up here, any ideas what?

Thanks for your help!

Edited by Orangeatang

##### Share on other sites

This is the scene I'm trying to render with shadows. The light (directional) is positioned at (0.0f, 0.0f, -150.0f), so the two pyramids at the front should be fully lit in the z-axis. The back two pyramids should be mostly in shadow:

This is the shadow map I'm generating from the light's position :

The shadow map looks mostly right (I mucked around with the histogram settings in PIX to get the contrast you see above), not sure why the top of the front pyramid is getting chopped off... but at least the relative depths look correct.

I seem to be just getting a value of 1.0f out of the shadow map buffer, because when I multiply the diffuse light by the depth that I'm reading out of the shadow map buffer (just to test things out), I'm getting the original scene.

So I'm guessing something is messed up with the way I'm converting world space position to light-screen space?

Edited by Orangeatang

##### Share on other sites

It thought it might be the byte alignment for my directional light shader's constant buffer... looking at it in PIX some of the values seem off ( (0.0f, 1.0f, 1.0f) instead of (1.0f, 1.0f, 1.0f) for the light colour), but I can read the ones that look wrong without any data errors. They also look fine when looking at the buffer in Intel GPA.

This is the structure of the constant buffer I'm passing in :

struct LightingConstants
{
DirectX::XMFLOAT3		myLightDirection;
ALIGN_16 DirectX::XMFLOAT3	myLightColour;
ALIGN_16 DirectX::XMFLOAT3	myCameraPosition;
DirectX::XMMATRIX		myInverseViewProjection;
DirectX::XMMATRIX		myLightViewProjection;
};


##### Share on other sites

Just to be on the safe side I changed the buffer to this :

struct LightingConstants
{
DirectX::XMFLOAT3	myLightDirection;
DirectX::XMFLOAT3	myLightColour;
DirectX::XMFLOAT3	myCameraPosition;
DirectX::XMMATRIX	myInverseViewProjection;
DirectX::XMMATRIX	myLightViewProjection;
};


So the constant buffer looks correct in PIX, but I'm still messing up the shadows.

##### Share on other sites

I've made a couple of improvements to the way my depth maps (from the light perspective) are made.

First off the render target I'm using is 1024x1024 instead of 1024x768, so the way I'm calculating the world/view/projection matrices for the light make much more sense :

XMMATRIX world = XMMatrixIdentity();
XMMATRIX view  = XMMatrixLookAtLH( XMLoadFloat3(&aLight->GetPosition()), XMLoadFloat3(&lightDirection), XMLoadFloat3(&XMFLOAT3(0.0f, 1.0f, 0.0f)) );
XMMATRIX projection = XMMatrixPerspectiveFovLH( (float)(XM_PI / 2.0f), 1.0f, 1.0f, 1024.0f );


In this instance the world matrix is always the identity matrix. The result of the light-depth pass looks like this :

But I can't seem to get the shadow calculation correct in the deferred directional light pixel shader.

I render the directional light using a full screen quad, and then reconstruct the world space position of the current vertex like this :

// Get the depth value
float currentDepth = DepthMap.Sample( DepthSampler, anInput.myTexCoords ).r;

// Calculate the screen-space position
float4 currentPosition;
currentPosition.x = anInput.myTexCoords.x * 2.0f - 1.0f;
currentPosition.y = -(anInput.myTexCoords.y * 2.0f - 1.0f);
currentPosition.z = currentDepth;
currentPosition.w = 1.0f;

// Transform the screen space position in to world-space
currentPosition = mul( currentPosition, myInverseViewProjection );
currentPosition /= currentPosition.w;


Then I convert the world position in to light space :

// Transform the world space position in to light space
float4 lightScreenPosition = mul( currentPosition, myLightViewProjection );
lightScreenPosition       /= lightScreenPosition.w;


The light view projection matrix is the same as the on I use for the light-depth buffer. Next I calculate the texture coordinates to use when sampling from the light depth buffer :

// Calculate the light texture coordinates.
float2 lightTexCoords;
lightTexCoords.x =  lightScreenPosition.x / 2.0f + 0.5f;
lightTexCoords.y =  lightScreenPosition.y / 2.0f + 0.5f;


Then I compare the depth of the current position in light space with the depth sampled from the light-depth map. If the depth of the light-space position is greater then I assume the pixel is lit :

// Sample the shadow map depth value from the depth texture using the sampler at the projected texture coordinate location.

// Calculate the depth of the light.
float lightDepthValue = lightScreenPosition.z;
lightDepthValue       = lightDepthValue - 0.001f;

// Compare the depth of the shadow map value and the depth of the light to determine whether to shadow or to light this pixel.
// If the light is in front of the object then light the pixel, if not then shadow this pixel since an object (occluder) is casting a shadow on it.
if( lightDepthValue < depthValue )
{
return float4( 1.0f, 1.0f, 1.0f, 1.0f );
}

return float4( 0.0f, 0.0f, 0.0f, 1.0f );


But instead of getting pyramids at the front of the scene nicely lit, and the pyramids at the back mostly in shadow I get this horrible mess :

Any ideas what I'm doing wrong here - I'm guessing it has to be something to do with the light-space position calculations (I can render directional lights without shadow mapping with a very similar shader), but I'm banging my head against a brick wall trying to figure out what the problem is

Thanks!

Edited by Orangeatang

##### Share on other sites

Fixed!

I had a number of issues for those of you interested :

• I was using a different format for my light-depth render target and my scene-depth render target
• I was clearing my light-depth render target to black, not white
• I wasn't flipping the y-axis coordinate in my light-depth texture lookup

• 13
• 18
• 29
• 11
• 27