I'm attempting to add directional shadow mapping to my terrain project, but I'm encountering a few issues. For reference, I'm following the RasterTek shadows tutorial.
The tutorial essentially follows the process of: Create a light > Create a depth texture based on the lights view > Render models and apply shadow shader.
The main issue I'm struggling with is how the tutorial handles the light. It essentially simulates a position and creates an ortho and view matrix. The issue seems to be escalating from how the light is set up. For a simple test, I created a plane, and set the light direction directly down, so everything should be lit, however, the following happens:
And when terrain is generated:
Here is code from a few areas that I think would be useful:
Light set up
mLight->SetPosition(XMFLOAT3(10.0f, 30.0f, -0.1f));
mLight->SetLookAt(XMFLOAT3(-10.0f, 0.0f, 0.0f));
mLight->GenerateOthoMatrix(40.0f, 1.0f, 50.0f);
Light GenerateOthoMatrix
void GenerateOthoMatrix(float width, float nearClip, float farClip)
{
mOrthoMatrix = XMMatrixOrthographicLH(width, width, nearClip, farClip);
}
Light GenerateViewMatrix
XMVECTOR up = XMVectorSet(0.0f, 1.0f, 0.0f, 0.0f);
XMVECTOR pos = XMLoadFloat3(&mPosition);
XMVECTOR la = XMLoadFloat3(&mLookAt);
mViewMatrix = XMMatrixLookAtLH(pos, la, up);
Depth Render Pass
mRenderTexture->SetRenderTarget(mGraphicsDevice->GetContext());
mRenderTexture->ClearRenderTarget(mGraphicsDevice->GetContext());
mLight->GenerateViewMatrix();
mDepthShader.Info.worldMatrix = mTerrain->GetWorldMatrix();
mDepthShader.Info.viewMatrix = mLight->GetViewMatrix();
mDepthShader.Info.projMatrix = mLight->GetOrthoMatrix();
mTerrain->Render(mGraphicsDevice->GetContext());
mDepthShader.Render(mGraphicsDevice->GetContext(), mTerrain->GetIndexCount());
mGraphicsDevice->ResetBackBuffer();
mGraphicsDevice->ResetViewport();
Shader Render calls simply map the 'info' settings to constant buffers and then call their relative vertex/pixel shaders.
Terrain Render calls just setup Index/Vertex buffers and topology, ready for the shader DrawIndexed.
RenderTexture is essentially a second viewport to render to and get a depth texture from
Main Render Pass
mTerrain->Render(mGraphicsDevice->GetContext());
mLight->GenerateViewMatrix();
mShader.Info.lightProj = mLight->GetOrthoMatrix();
mShader.Info.lightView = mLight->GetViewMatrix();
mShader.Info.depthTex = mRenderTexture->GetSRV();
mShader.Render(mGraphicsDevice->GetContext(), mTerrain->GetIndexCount());
Depth Vertex Shader
cbuffer SPerFrameCB : register(b0)
{
matrix worldMatrix;
matrix viewMatrix;
matrix projMatrix;
};
struct VertexIn
{
float4 Pos : POSITION;
};
struct VertexOut
{
float4 Pos : SV_POSITION;
float4 DPos : TEXTURE0;
};
VertexOut main(VertexIn vin)
{
VertexOut vout;
vin.Pos.w = 1.0f;
vout.Pos = mul(vin.Pos, worldMatrix);
vout.Pos = mul(vout.Pos, viewMatrix);
vout.Pos = mul(vout.Pos, projMatrix);
vout.DPos = vout.Pos;
return vout;
}
Depth Pixel Shader
struct PixelIn
{
float4 Pos : SV_POSITION;
float4 DPos : TEXTURE0;
};
float4 main(PixelIn pin) : SV_Target
{
float depthVal = pin.DPos.z / pin.DPos.w;
float4 colour = float4(depthVal, depthVal, depthVal, 1.0f);
return colour;
}
Shadow Vertex Shader
cbuffer SPerFrameCB : register(b0)
{
matrix worldMatrix;
matrix viewMatrix;
matrix projMatrix;
matrix lightViewMatrix;
matrix lightProjMatrix;
};
struct VertexIn
{
float4 Pos : POSITION;
float2 Tex : TEXCOORD0;
float3 Normal : NORMAL;
};
struct VertexOut
{
float4 Pos : SV_POSITION;
float2 Tex : TEXCOORD0;
float3 Normal : NORMAL;
float4 LightV : TEXCOORD1;
};
VertexOut main(VertexIn vin)
{
VertexOut vout;
vin.Pos.w = 1.0f;
float4 worldPos = mul(vin.Pos, worldMatrix);
vout.Pos = worldPos;
vout.Pos = mul(vout.Pos, viewMatrix);
vout.Pos = mul(vout.Pos, projMatrix);
vout.LightV = worldPos;
vout.LightV = mul(vout.LightV, lightViewMatrix);
vout.LightV = mul(vout.LightV, lightProjMatrix);
vout.Tex = vin.Tex;
vout.Normal = mul(vin.Normal, (float3x3)worldMatrix);
vout.Normal = normalize(vout.Normal);
return vout;
}
Shadow Pixel Shader
Texture2D shaderTexture;
Texture2D lowerTex : register(t0);
Texture2D mediumTex : register(t1);
Texture2D higherTex : register(t2);
Texture2D depthTex : register(t3);
SamplerState SampleTypeClamp : register(s0);
SamplerState SampleTypeWrap : register(s1);
cbuffer SPerLightCB : register(b0)
{
float4 ambientColour;
float4 diffuseColour;
float3 lightDirection;
float padding;
};
struct PixelIn
{
float4 Pos : SV_POSITION;
float2 Tex : TEXCOORD0;
float3 Normal : NORMAL;
float4 LightV : TEXCOORD1;
};
float4 main(PixelIn pin) : SV_Target
{
float bias = 0.001f;
float3 lightDir = -lightDirection;
float4 colour = ambientColour;
float2 projTexCoord;
projTexCoord.x = pin.LightV.x / pin.LightV.w / 2.0f + 0.5f;
projTexCoord.y = -pin.LightV.y / pin.LightV.w / 2.0f + 0.5f;
if ((saturate(projTexCoord.x) == projTexCoord.x) && (saturate(projTexCoord.y) == projTexCoord.y))
{
float depthVal = depthTex.Sample(SampleTypeClamp, projTexCoord).r;
float lightDepthVal = pin.LightV.z / pin.LightV.w;
lightDepthVal -= bias;
if (lightDepthVal < depthVal)
{
float lightIntensity = saturate(dot(pin.Normal, lightDir));
if (lightIntensity > 0.0f)
{
colour += diffuseColour * lightIntensity;
colour = saturate(colour);
}
}
}
float4 lowerColour = lowerTex.Sample(SampleTypeWrap, pin.Tex);
float4 mediumColour = mediumTex.Sample(SampleTypeWrap, pin.Tex);
float4 higherColour = higherTex.Sample(SampleTypeWrap, pin.Tex);
float4 texColour;
float slope = 1.0f - pin.Normal.y, bVal;
if (slope < 0.4f)
{
bVal = slope / 0.4f;
texColour = lerp(lowerColour, mediumColour, bVal);
}
if (slope >= 0.4f && slope < 0.6f)
{
bVal = (slope - 0.4f) * (1.0f / (0.6f - 0.4f));
texColour = lerp(mediumColour, higherColour, bVal);
}
if (slope >= 0.6f)
{
texColour = higherColour;
}
colour *= texColour;
return colour;
}
I'm very sorry for the large amounts of code - I'm not sure which sections would help best in identifying the issue. If anyone could help, or provide a shadow mapping resource I would be very grateful. There doesn't seem to be many shadow mapping resources, or at least I haven't been able to find many.