Problem with shadow mapping: weird artifacts

Started by
6 comments, last by auto.magician 12 years, 5 months ago
I'm trying to implement shadow mapping but i'm getting some weird artifacts:

shadowm.png



Shadow map generation shader
cbuffer c_buffer
{
float4x4 LightViewProj;
}

struct VS_OUT
{
float4 Pos : SV_POSITION;
};

VS_OUT VShader(float4 position : POSITION, float4 normal : NORMAL)
{
VS_OUT output;
output.Pos = mul(LightViewProj, position);
return output;
}

void PShader(VS_OUT input) {}



Shadow mapping shader
cbuffer c_buffer
{
float4x4 World;
float4x4 WorldViewProj;
float4x4 LightViewProj;
float4 LightDirection;
float4 Ambient;
float4 Eye;
}

cbuffer material
{
float4 Diffuse;
float4 Specular;
float Shininess;
}

Texture2D shadowMap;
SamplerState pointSampler; // I'm not setting this in the application, i'm using the default sampler

struct VS_OUT
{
float4 Pos : SV_POSITION;
float4 lpos : TEXCOORD0;
float3 Nor : NORMAL;
float3 Half : TEXCOORD2;
};

VS_OUT VShader(float4 position : POSITION, float4 normal : NORMAL)
{
VS_OUT output = (VS_OUT)0;

output.Pos = mul(WorldViewProj, position);
output.Nor = normalize(mul(normal, World)).xyz;

// Store worldspace position projected to light clip space
output.lpos = mul(LightViewProj, position);

float3 view = normalize(Eye - mul(World, position)).xyz;
output.Half = normalize(LightDirection.xyz + view);

return output;
}

float4 PShader(VS_OUT input) : SV_TARGET
{
//re-homogenize position after interpolation
input.lpos.xyz /= input.lpos.w;

//if position is not visible to the light - dont illuminate it
//results in hard light frustum
if( input.lpos.x < -1.0f || input.lpos.x > 1.0f ||
input.lpos.y < -1.0f || input.lpos.y > 1.0f ||
input.lpos.z < 0.0f || input.lpos.z > 1.0f ) return Ambient;

//transform clip space coords to texture space coords (-1:1 to 0:1)
input.lpos.x = input.lpos.x/2 + 0.5;
input.lpos.y = input.lpos.y/-2 + 0.5;

//sample shadow map - point sampler
float shadowMapDepth = shadowMap.Sample(pointSampler, input.lpos.xy).r;

//if clip space z value greater than shadow map value then pixel is in shadow
if (shadowMapDepth < input.lpos.z) return Ambient;

//otherwise calculate ilumination at fragment
input.Nor = normalize(input.Nor);
input.Half = normalize(input.Half);

float4 TotalDiffuse = Diffuse * saturate(dot(input.Nor, LightDirection.xyz));
float4 TotalSpecular = Specular * pow(saturate(dot(input.Nor, input.Half)), Shininess);

return Ambient + TotalDiffuse + TotalSpecular;
}



How i generate shadow map resources
//=====================================================================================================
// Create shadow map resources
//=====================================================================================================
void Framework::CreateShadowMap()
{
// Create shadow map texture desc
D3D11_TEXTURE2D_DESC td;
ZeroMemory(&td, sizeof(td));
td.Width = mClientSize.cx;
td.Height = mClientSize.cy;
td.MipLevels = 1;
td.ArraySize = 1;
td.Format = DXGI_FORMAT_R32_TYPELESS;
td.SampleDesc.Count = 1;
td.BindFlags = D3D11_BIND_DEPTH_STENCIL | D3D11_BIND_SHADER_RESOURCE;

// Create the depth stencil view desc
D3D11_DEPTH_STENCIL_VIEW_DESC dsvd;
ZeroMemory(&dsvd, sizeof(dsvd));
dsvd.Format = DXGI_FORMAT_D32_FLOAT;
dsvd.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2D;

// Create shader resource view desc
D3D11_SHADER_RESOURCE_VIEW_DESC srvd;
ZeroMemory(&srvd, sizeof(srvd));
srvd.Format = DXGI_FORMAT_R32_FLOAT;
srvd.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
srvd.Texture2D.MipLevels = td.MipLevels;

// Create texture and depth/resource views
mShadowMap = mRenderer->CreateTexture2D(td);
mShadowMapDepthView = mRenderer->CreateDepthStencilView(mShadowMap, dsvd);
mShadowMapSRView = mRenderer->CreateShaderResourceView(mShadowMap, srvd);
}



And finally the rendering..
//=====================================================================================================
// Render stuff
//=====================================================================================================
bool Framework::OnRenderStart()
{
D3DXMATRIX view, proj, world, rotX;
D3DXMatrixIdentity(&world);

D3DXMatrixRotationX(&rotX, D3DXToRadian(90));
world = rotX;

// create a projection matrix
D3DXMatrixPerspectiveFovLH(&proj,
(FLOAT)D3DXToRadian(45),
(FLOAT)mClientSize.cx / (FLOAT)mClientSize.cy,
1.0f,
1000.0f);

// View matrix
D3DXMATRIX lView, lProj;
D3DXMatrixLookAtLH(&lView, &mLight.Position, &mLight.Direction, &D3DXVECTOR3(0, 1, 0));

// Compute the light projection matrix
D3DXMatrixOrthoLH(&lProj, 45.0f, 45.0f, 1.0f, 1024.0f);

// Compute the light-view-projection matrix
D3DXMATRIX LightViewProj = world * lView * lProj;

// Shadow pass
mRenderer->SetRenderTargets(0, 0, mShadowMapDepthView);
mRenderer->ClearDepthStencilView(mShadowMapDepthView);

mEffectDepth->SetShaderMatrix("LightViewProj", LightViewProj);

for (int i = 0; i < mVisibleMeshes.size(); i++)
{
mRenderer->DrawIndexed(mEffectDepth,
mVisibleMeshes->mVertexBuffer,
mVisibleMeshes->mIndexBuffer,
mVisibleMeshes->mInputLayout,
mVisibleMeshes->IndexCount,
0,
D3D10_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
}

// Render
mRenderer->SetRenderTargets(1, &mRenderTarget, mZBuffer);
mRenderer->ClearRenderTargetView(mRenderTarget);
mRenderer->ClearDepthStencilView(mZBuffer);

mEffect->SetShaderMatrix("World", world);
mEffect->SetShaderMatrix("WorldViewProj", world * mCamera.GetViewMatrix(view) * proj);
mEffect->SetShaderVector("Ambient", D3DXVECTOR3(0.2f, 0.2f, 0.2f));
mEffect->SetShaderVector("Eye", mCamera.GetPosition());
mEffect->SetShaderVector("LightDirection", mLight.Direction);
mEffect->SetShaderMatrix("LightViewProj", LightViewProj);
mEffect->SetShaderTexture("shadowMap", mShadowMapSRView);

if (mVisibleMeshes[0]->mMaterial)
{
mVisibleMeshes[0]->mEffect->SetShaderVector("Diffuse", mVisibleMeshes[0]->mMaterial->Diffuse);
mVisibleMeshes[0]->mEffect->SetShaderVector("Specular", mVisibleMeshes[0]->mMaterial->Specular);
mVisibleMeshes[0]->mEffect->SetShaderFloat("Shininess", mVisibleMeshes[0]->mMaterial->Shininess);
}

for (int i = 0; i < mVisibleMeshes.size(); i++)
{
mRenderer->DrawIndexed(mVisibleMeshes->mEffect,
mVisibleMeshes->mVertexBuffer,
mVisibleMeshes->mIndexBuffer,
mVisibleMeshes->mInputLayout,
mVisibleMeshes->IndexCount,
0,
D3D10_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
}

ID3D11ShaderResourceView *const pSRV[1] = {NULL};
mRenderer->mImmediateContext->PSSetShaderResources(0, 1, pSRV);

return true;
}
"Spending your life waiting for the messiah to come save the world is like waiting around for the straight piece to come in Tetris...even if it comes, by that time you've accumulated a mountain of shit so high that you're fucked no matter what you do. "
Advertisement
That looks like problem with precision. You should use some epsilon value when comparing the depths:


//if clip space z value greater than shadow map value then pixel is in shadow
if (shadowMapDepth < input.lpos.z) return Ambient;


change into


//if clip space z value greater than shadow map value then pixel is in shadow
if (shadowMapDepth + EPSILON < input.lpos.z) return Ambient;


where EPSILON is a small number, for example 0.005f. You'll get the right value by some testing - too high value will make some smaller shadows disappear and all shadows will "move" a bit away from the source, too small value will make artifacts.

It also helps to have higher pixel resolution and higher bit depth of the shadow map texture. But you'll never get perfect result without the EPSILON because the computations will never be perfect.
Imagine a perfectly even plane, lit from an angle. Because the shadow map can hold only for example 32bit values, it is not a continuous range of possible depths. So, the representation of the sufrace in the shadow map is like if the sufrace had small steps on it.
Something similar goes for the depth you calculate in your shader and then you compare them. When you are on the boundary case where the depth in the shadow map should be EXACTLY the same as the actual depth, then you'll have semi-randomly some pixels decided to be in shadow and some not and the object will partly self-shadow.
Hey thanks it worked! I used a value of 0.00005. But i don't understand what the problem is..

EDIT: Oh... thanks for the explanation :)

shadow2a.png
"Spending your life waiting for the messiah to come save the world is like waiting around for the straight piece to come in Tetris...even if it comes, by that time you've accumulated a mountain of shit so high that you're fucked no matter what you do. "
I added the explanation to my post later, maybe it wasn't there yet when you were posting yours.
Yeah i edited mine before you posted about editing yours :P
"Spending your life waiting for the messiah to come save the world is like waiting around for the straight piece to come in Tetris...even if it comes, by that time you've accumulated a mountain of shit so high that you're fucked no matter what you do. "

Yeah i edited mine before you posted about editing yours :P


LOL that's the magic of the internet forums :lol::lol:

Nice statue, btw.
Just something i got off the net.. Lost the link though
"Spending your life waiting for the messiah to come save the world is like waiting around for the straight piece to come in Tetris...even if it comes, by that time you've accumulated a mountain of shit so high that you're fucked no matter what you do. "
Hiya,

Hehe, If i may butt in here...
I used the same statue model when I learned shadow mapping :P, although the base is different.
It's available in .x form in the 'source code' link at the bottom of this tutorial page :-


Shadow Mapping Tutorial

It is an awesome model.

This topic is closed to new replies.

Advertisement