I got CSM to work in OpenGL, and now Im trying to do the same in directx. I'm using the same math library and all and I'm pretty much using the alghorithm straight off. I am using right-handed, column major matrices from GLM.
The light is looking (-1, -1, -1).
The problem I have is twofolds;
For some reason, the ground floor is causing alot of (false) shadow artifacts, like the vast shadowed area you see. I confirmed this when I disabled the ground for the depth pass, but thats a hack more than anything else
The shadows are inverted compared to the shadowmap. If you squint you can see the chairs shadows should be mirrored instead.
This is the first cascade shadow map, in range of the alien and the chair:
I can't figure out why this is. This is the depth pass:
for (uint32_t cascadeIndex = 0; cascadeIndex < NUM_SHADOWMAP_CASCADES; cascadeIndex++)
{
mShadowmap.BindDepthView(context, cascadeIndex);
CameraFrustrum cameraFrustrum = CalculateCameraFrustrum(degreesFOV, aspectRatio, nearDistArr[cascadeIndex], farDistArr[cascadeIndex], cameraViewMatrix);
lightVPMatrices[cascadeIndex] = CreateDirLightVPMatrix(cameraFrustrum, lightDir);
mVertexTransformPass.RenderMeshes(context, renderQueue, meshes, lightVPMatrices[cascadeIndex]);
lightVPMatrices[cascadeIndex] = gBiasMatrix * lightVPMatrices[cascadeIndex];
farDistArr[cascadeIndex] = -farDistArr[cascadeIndex];
}
CameraFrustrum CalculateCameraFrustrum(const float fovDegrees, const float aspectRatio, const float minDist, const float maxDist, const Mat4& cameraViewMatrix)
{
CameraFrustrum ret = { Vec4(1.0f, 1.0f, -1.0f, 1.0f), Vec4(1.0f, -1.0f, -1.0f, 1.0f), Vec4(-1.0f, -1.0f, -1.0f, 1.0f), Vec4(-1.0f, 1.0f, -1.0f, 1.0f),
Vec4(1.0f, -1.0f, 1.0f, 1.0f), Vec4(1.0f, 1.0f, 1.0f, 1.0f), Vec4(-1.0f, 1.0f, 1.0f, 1.0f), Vec4(-1.0f, -1.0f, 1.0f, 1.0f), };
const Mat4 perspectiveMatrix = PerspectiveMatrixFov(fovDegrees, aspectRatio, minDist, maxDist);
const Mat4 invMVP = glm::inverse(perspectiveMatrix * cameraViewMatrix);
for (Vec4& corner : ret)
{
corner = invMVP * corner;
corner /= corner.w;
}
return ret;
}
Mat4 CreateDirLightVPMatrix(const CameraFrustrum& cameraFrustrum, const Vec3& lightDir)
{
Mat4 lightViewMatrix = glm::lookAt(Vec3(0.0f), -glm::normalize(lightDir), Vec3(0.0f, -1.0f, 0.0f));
Vec4 transf = lightViewMatrix * cameraFrustrum[0];
float maxZ = transf.z, minZ = transf.z;
float maxX = transf.x, minX = transf.x;
float maxY = transf.y, minY = transf.y;
for (uint32_t i = 1; i < 8; i++)
{
transf = lightViewMatrix * cameraFrustrum[i];
if (transf.z > maxZ) maxZ = transf.z;
if (transf.z < minZ) minZ = transf.z;
if (transf.x > maxX) maxX = transf.x;
if (transf.x < minX) minX = transf.x;
if (transf.y > maxY) maxY = transf.y;
if (transf.y < minY) minY = transf.y;
}
Mat4 viewMatrix(lightViewMatrix);
viewMatrix[3][0] = -(minX + maxX) * 0.5f;
viewMatrix[3][3] = -(minY + maxY) * 0.5f;
viewMatrix[3][4] = -(minZ + maxZ) * 0.5f;
viewMatrix[0][3] = 0.0f;
viewMatrix[1][3] = 0.0f;
viewMatrix[2][3] = 0.0f;
viewMatrix[3][3] = 1.0f;
Vec3 halfExtents((maxX - minX) * 0.5, (maxY - minY) * 0.5, (maxZ - minZ) * 0.5);
return OrthographicMatrix(-halfExtents.x, halfExtents.x, -halfExtents.y, halfExtents.y, halfExtents.z, -halfExtents.z) * viewMatrix;
}
And this is the pixel shader used for the lighting stage:
#define DEPTH_BIAS 0.0005
#define NUM_CASCADES 4
cbuffer DirectionalLightConstants : register(CBUFFER_REGISTER_PIXEL)
{
float4x4 gSplitVPMatrices[NUM_CASCADES];
float4x4 gCameraViewMatrix;
float4 gSplitDistances;
float4 gLightColor;
float4 gLightDirection;
};
Texture2D gPositionTexture : register(TEXTURE_REGISTER_POSITION);
Texture2D gDiffuseTexture : register(TEXTURE_REGISTER_DIFFUSE);
Texture2D gNormalTexture : register(TEXTURE_REGISTER_NORMAL);
Texture2DArray gShadowmap : register(TEXTURE_REGISTER_DEPTH);
SamplerComparisonState gShadowmapSampler : register(SAMPLER_REGISTER_DEPTH);
float4 ps_main(float4 position : SV_Position) : SV_Target0
{
float4 worldPos = gPositionTexture[uint2(position.xy)];
float4 diffuse = gDiffuseTexture[uint2(position.xy)];
float4 normal = gNormalTexture[uint2(position.xy)];
float4 camPos = mul(gCameraViewMatrix, worldPos);
uint index = 3;
if (camPos.z > gSplitDistances.x)
index = 0;
else if (camPos.z > gSplitDistances.y)
index = 1;
else if (camPos.z > gSplitDistances.z)
index = 2;
float3 projCoords = (float3)mul(gSplitVPMatrices[index], worldPos);
float viewDepth = projCoords.z - DEPTH_BIAS;
projCoords.z = float(index);
float visibilty = gShadowmap.SampleCmpLevelZero(gShadowmapSampler, projCoords, viewDepth);
float angleNormal = clamp(dot(normal, gLightDirection), 0, 1);
return visibilty * diffuse * angleNormal * gLightColor;
}
As you can see I am using depth bias and a bias matrix.
Any hints on why this behaves so wierdly?
EDIT:
I've rolled my own projective matrices, based of http://msdn.microsoft.com/en-us/library/windows/desktop/bb205348(v=vs.85).aspx but column major instead of row major:
Mat4 OrthographicMatrix(const float left, const float right, const float top, const float bottom, const float zNear, const float zFar)
{
Mat4 ret(1.0f);
ret[0][0] = 2.0f / (right - left);
ret[1][5] = 2.0f / (top - bottom);
ret[2][6] = 1.0f / (zNear - zFar);
ret[3][0] = (left + right) / (left - right);
ret[3][7] = (top + bottom) / (bottom - top);
ret[3][8] = (zNear) / (zNear - zFar);
return ret;
}
EDIT #2: The new bias matrix:
const Mat4 gBiasMatrix(0.5f, 0.0f, 0.0f, 0.0f,
0.0f, -0.5f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f,
0.5f, -0.5f, 1.0f, 1.0f);
Dosn't fix the shadow artifacts though - but the mirror issue yes