D3D11 Shadow Mapping problems

Started by
5 comments, last by kauna 12 years, 8 months ago
Hi!

I've been trying to implement Shadow Mapping on Direct3D11 for a few days and I've got the algoritm so far but it seems like I'm having some problem with the Render To Texture part.


What happens is that everythins shows OK (gray) if I render my first Shadow Mapping pass to the screen (for debugging) but the entire map is WHITE if I render to my Depth Stencil View. I mean, there's no Shadow or any color change in my scene as if the entire texture was white or as if the sampler wout not be working.

My personal suspicions are:
  1. The sampler state creation code is messed up;
  2. The texture is not being (property) bound to the pipeline.
  3. Some depth buffer clearing problem
  4. Some depth testing problem when writing to the texture;

But I don't know since I've never done Shadow Mapping before ......... sad.gif

I would really appreciate if anyone would help me find what's wrong with this thing.

TIA
The following part is just about code.



My hobbyst game code is available in Google Code if anyone wanna see it:
http://code.google.com/p/game-code/ (Yeah, bad name .. I know!)


This project is made of a Static Library project where all the Direct3D11 stuff is and an EXE where I test it on a very simple Pong game.
I will post pieces of code from both below but here are the most relevant files:


The Direct3DManager Class
http://code.google.c...CodeInternals.h
http://code.google.c...sManagement.cpp
This is my main Direct3D class which tracks my shadow mapping resources long many others. This class starts on line 168 of the .h file and all around the .cpp file.

My shaders are all in this file:
http://code.google.c...ers/standard.fx


OK, the important parts of the CPP file IMHO are:




#pragma region Shadow Mapping Initialization


// Create depth stencil texture

D3D11_TEXTURE2D_DESC dtd =
{
width,//UINT Width;
height,//UINT Height;
1,//UINT MipLevels;
1,//UINT ArraySize;
DXGI_FORMAT_R32_TYPELESS,//DXGI_FORMAT Format;
1,//DXGI_SAMPLE_DESC SampleDesc;
0,
D3D11_USAGE_DEFAULT,//D3D11_USAGE Usage;
D3D11_BIND_DEPTH_STENCIL|D3D11_BIND_SHADER_RESOURCE,//UINT BindFlags;
0,//UINT CPUAccessFlags;
0//UINT MiscFlags;
};


hr = _pd3dDevice->CreateTexture2D( &dtd, NULL, &_pShadowMapTexture );
if( FAILED( hr ) )
return false;

D3D11_DEPTH_STENCIL_VIEW_DESC dsvd =
{
DXGI_FORMAT_D32_FLOAT,
D3D11_DSV_DIMENSION_TEXTURE2D,
0
};

hr = _pd3dDevice->CreateDepthStencilView(_pShadowMapTexture, &dsvd, &_pShadowMapDSV);

if( FAILED( hr ) )
return false;

D3D11_SHADER_RESOURCE_VIEW_DESC dsrvd =
{
DXGI_FORMAT_R32_FLOAT,
D3D11_SRV_DIMENSION_TEXTURE2D,
0,
0
};
dsrvd.Texture2D.MipLevels = 1;

hr = _pd3dDevice->CreateShaderResourceView(_pShadowMapTexture, &dsrvd, &_pShadowMapView);
if( FAILED( hr ) )
return false;


#pragma endregion



For some reason I've placed the SamplerState creation code down below:



D3D11_SAMPLER_DESC SamDescShad =
{
D3D11_FILTER_COMPARISON_MIN_MAG_LINEAR_MIP_POINT,// D3D11_FILTER Filter;
D3D11_TEXTURE_ADDRESS_BORDER, //D3D11_TEXTURE_ADDRESS_MODE AddressU;
D3D11_TEXTURE_ADDRESS_BORDER, //D3D11_TEXTURE_ADDRESS_MODE AddressV;
D3D11_TEXTURE_ADDRESS_BORDER, //D3D11_TEXTURE_ADDRESS_MODE AddressW;
0,//FLOAT MipLODBias;
0,//UINT MaxAnisotropy;
D3D11_COMPARISON_LESS , //D3D11_COMPARISON_FUNC ComparisonFunc;
0.0,0.0,0.0,0.0,//FLOAT BorderColor[ 4 ];
0,//FLOAT MinLOD;
0//FLOAT MaxLOD;
};


/*SamDescShad.MaxAnisotropy = 15;
SamDescShad.AddressU = D3D11_TEXTURE_ADDRESS_CLAMP;
SamDescShad.AddressV = D3D11_TEXTURE_ADDRESS_CLAMP;
SamDescShad.Filter = D3D11_FILTER_ANISOTROPIC;
SamDescShad.ComparisonFunc = D3D11_COMPARISON_NEVER;*/

hr = _pd3dDevice->CreateSamplerState( &sampDesc, &_pShadowMapSampler );
if( FAILED( hr ) )
return false;



My Light -> Object -> Projection code is in this function:



void Direct3DManager::setShadowMapTarget(XMFLOAT3& objectPos)
{

if(_light.position.x || _light.position.y || _light.position.z)
{


XMVECTOR eye = XMVectorSet( _light.position.x, _light.position.y, _light.position.z, 0);
XMVECTOR at = XMVectorSet( objectPos.x, objectPos.y, objectPos.z, 0);

//at = XMVectorSet(0, 0, 0, 0);

const XMVECTOR up = XMVectorSet(0,1,0,0);

XMMATRIX projectionMatrix = XMMatrixPerspectiveFovLH( XM_PIDIV2, 1, 1, 1000);

_shaderGlobals.LightViewProjection = XMMatrixTranspose (
XMMatrixLookAtLH(eye, at, up) * projectionMatrix
);
}

}



And finally my Shadow Map is setup in these functions:



void Direct3DManager::beginShadowMapping()
{

_previousWorld = _worldMatrix;
_previousProj = _projectionMatrix;
_previousView = _viewMatrix;

ID3D11ShaderResourceView* nullSRV = 0;

_pImmediateContext->PSSetShaderResources(1, 1, &nullSRV);

_pImmediateContext->ClearDepthStencilView( _pShadowMapDSV, D3D11_CLEAR_DEPTH, 1.0, 0 );
_pImmediateContext->ClearDepthStencilView( _pDepthStencilView, D3D11_CLEAR_DEPTH, 1, 0);

ID3D11RenderTargetView* nullView = 0;
_pImmediateContext->OMSetRenderTargets( 1, &nullView, _pShadowMapDSV );


setTechnique(SHADER_TECHNIQUES_SHADOW_MAP);
}


void Direct3DManager::endShadowMapping()
{

ID3D11RenderTargetView* pNullView = 0;
_pImmediateContext->OMSetRenderTargets( 1, &pNullView, 0 );
setTechnique(SHADER_TECHNIQUES_DEFAULT);
_pImmediateContext->OMSetRenderTargets( 1, &_pRenderTargetView, _pDepthStencilView );
_pImmediateContext->ClearDepthStencilView( _pDepthStencilView, D3D11_CLEAR_DEPTH, 1, 0);
_pImmediateContext->PSSetShaderResources(1, 1, &_pShadowMapView);
_pImmediateContext->PSSetSamplers(1, 1, &_pShadowMapSampler);


updateWorld(_previousWorld);
updateView(_previousView);
updateProjection(_previousProj);



}

Advertisement
When the depth map was seen as white, I took a screenshot, increased contrast and saw that it contained depth information but everything was close to 1 because of the depth buffer's non linear representation when perspective was used.

I can't find something in your shader for converting between ZW scale and linear depth for the comparison.
Yeah depth buffer values for a perspective projection do not increase linearly with distance from the camera, so just displaying it won't look right. Rescaling it to display the [0.9, 1.0] range should make it look better. You can also convert it to view space z and divide by the far clip distance, which gives you a linear value that you can display.

Yeah depth buffer values for a perspective projection do not increase linearly with distance from the camera, so just displaying it won't look right. Rescaling it to display the [0.9, 1.0] range should make it look better. You can also convert it to view space z and divide by the far clip distance, which gives you a linear value that you can display.


Yeah I saw that Z / Max Depth practice on Riemers's tutorials. But my problem is not about incorrect shadow casting ... yet :(

I've done some more testing and found out some details:

1 - If I clear the Shadow Map's DSV to some value say 0.5 farther objects become black in the final scene.
2 - Outputting whatever value (say 0.0) from my Shadow Mapping Pixel Shader won't change anything on the final scene.

These two thing make me believe that the Shadow Map is not being writen to by the pixel shader or there's some kind of depth testing preventing the pipeline from rendering anything during that phase.

That's why I believe it's a setup problem not a shader problem .........

EDIT: This is the screenshot proced with a clear depth of 0.5 and the following instruction in my Pixel Shader:
float light = shadowMapTex.Sample(samShadow, shadowPos.xy).r + 0.008 > depth ? 1 : 0.25;
Solved!

My shader wasn't (explicitly) writing SV_Depth values, just SV_Target values as I saw in many Shader Model 2.0 tutorials!

Last, but not least, I still need to fix some stuff as you can see in the screenshot.

Does anyone have any idea?



cbuffer cbGlobals : register (b0)
{
matrix WorldViewProjection;
matrix WorldOnly;
matrix LightViewProjection;
float MaxDepth;

};

SamplerState samLinear : register(s0);
SamplerState samShadow : register(s1);

Texture2D diffuseTex : register( t0 );
Texture2D shadowMapTex : register( t1 );

struct AppToVS
{
float4 Pos : POSITION;
float2 Tex : TEXCOORD;
float3 Normal : NORMAL;

};

struct SM_VSToPS
{
float4 Pos : SV_POSITION;
float2 Depth: TEXCOORD0;

};
struct SM_VSToFrame
{
float Depth : SV_Depth;
};

struct RenderVSToPS
{
float4 Pos : SV_POSITION;
float2 Tex : TEXCOORD0;
float4 ShadowCoords : TEXCOORD1;
float4 WorldPosition : TEXCOORD2;
float4 Normal :TEXCOORD3;
};

struct RenderPSToFrame
{
float4 Color: SV_Target;
};

SM_VSToPS ShadowMapVS( AppToVS In)
{
SM_VSToPS Out = (SM_VSToPS)0;


Out.Pos = mul ( mul (In.Pos, WorldOnly ), LightViewProjection);



Out.Depth = Out.Pos.zw;
return Out;
}

SM_VSToFrame ShadowMapPS( SM_VSToPS In )
{
float t = In.Pos.z / In.Pos.w;

SM_VSToFrame Out = (SM_VSToFrame)0;

Out.Depth = t;

return Out;
}



RenderVSToPS RenderVS( AppToVS In)
{
RenderVSToPS Out = (RenderVSToPS)0;
Out.Pos = mul ( In.Pos, WorldViewProjection );
Out.Tex = In.Tex;

float4x4 worldLightViewProjection = mul (WorldOnly, LightViewProjection);
Out.ShadowCoords = mul( mul( In.Pos, WorldOnly), LightViewProjection);
Out.WorldPosition = mul(In.Pos, WorldOnly);

Out.Normal = mul( float4(In.Normal, 0), WorldViewProjection);



return Out;
}

RenderPSToFrame RenderPS( RenderVSToPS In )
{
float4 shadowPos = In.ShadowCoords;
shadowPos.xy /= shadowPos.w;
shadowPos.x = 0.5 * shadowPos.x + 0.5;
shadowPos.y = -0.5 *shadowPos.y + 0.5;

float depth = shadowPos.z / shadowPos.w;

float light = shadowMapTex.Sample(samShadow, shadowPos.xy).r +0.008f > depth ? 1 : 0.25;

RenderPSToFrame Out = (RenderPSToFrame)0;
Out.Color = light * diffuseTex.Sample( samLinear, In.Tex);

return Out;
}

technique RenderTech
{
pass Shadow
{
SetVertexShader( CompileShader ( vs_4_0, ShadowMapVS()));
SetPixelShader ( CompileShader ( ps_4_0, ShadowMapPS()));
}

pass Render
{
SetVertexShader( CompileShader ( vs_4_0, RenderVS()));
SetPixelShader ( CompileShader ( ps_4_0, RenderPS()));
}

};

Solved!

My shader wasn't (explicitly) writing SV_Depth values, just SV_Target values as I saw in many Shader Model 2.0 tutorials!

Last, but not least, I still need to fix some stuff as you can see in the screenshot.

Does anyone have any idea?

<snip>

Working as intended, you're just seeing some undersampling artifacts. Try increasing the resolution of your shadow map some.
clb: At the end of 2012, the positions of jupiter, saturn, mercury, and deimos are aligned so as to cause a denormalized flush-to-zero bug when computing earth's gravitational force, slinging it to the sun.
Hi,

As far as I know, you should be able to do shadow mapping without a pixel shader (unless you need alpha transparency or some other special effect), ie. set pixel shader to null.

Also, you shouldn't need to explicitly output the SV_DEPTH value or need to do the division by W yourself when rendering the depth buffer.

Otherwise, to improve your shadow quality you may use SampleCmp or SampleCmpLevelZero

Cheers!

This topic is closed to new replies.

Advertisement