Jump to content
  • Advertisement
Sign in to follow this  
Waaayoff

Problem with shadow mapping: weird artifacts

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

If you intended to correct an error in the post then please contact us.

Recommended Posts

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;
}

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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

Share this post


Link to post
Share on other sites
I added the explanation to my post later, maybe it wasn't there yet when you were posting yours.

Share this post


Link to post
Share on other sites

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.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!