I'm doing cascaded shadow mapping and I have problem. From the cameras position, the splits are [camPos, 6], [camPos+6, 12], [camPos+12, 18] and [camPos+18, 100]. The "mSplitDistance" variable is the camera position + splitFarDistance (in camera space) for each split. So, standing at world pos 0.0f and looking down -Z, the values would be [-6, -12, -18, -100], and turn around and look down +Z means [6, 12, 18, 100]
The shadow mapping is only accurate in the last split, from 18 to 100; there the shadows behave as intended. In the first three splits, it is sometimes accurate and sometimes completely wrong.
I am using deferred renderering, so I am just drawing a quad and then go fragment per fragment and extract the world position / normals / etc from there.
I suspect there is somethign wrong in the lighting fragment shader. Here it is:
#version 430
layout(std140) uniform;
const float DEPTH_BIAS = 0.00005;
uniform UnifDirLight
{
mat4 mVPMatrix[4]; // the bias * crop * projection* view matrices for the directional light
float mSplitDistance[4];
vec4 mLightColor;
vec4 mLightDir;
vec4 mGamma;
vec2 mScreenSize;
} UnifDirLightPass;
layout (binding = 2) uniform sampler2D unifPositionTexture;
layout (binding = 3) uniform sampler2D unifNormalTexture;
layout (binding = 4) uniform sampler2D unifDiffuseTexture;
layout (binding = 6) uniform sampler2DArrayShadow unifShadowTexture;
out vec4 fragColor;
void main()
{
vec2 texcoord = gl_FragCoord.xy / UnifDirLightPass.mScreenSize;
vec3 worldPos = texture(unifPositionTexture, texcoord).xyz; // world position; in world space
vec3 normal = normalize(texture(unifNormalTexture, texcoord).xyz);
vec3 diffuse = texture(unifDiffuseTexture, texcoord).xyz;
int index = 3;
if (worldPos.z > UnifDirLightPass.mSplitDistance[0])
index = 0;
else if (worldPos.z > UnifDirLightPass.mSplitDistance[1])
index = 1;
else if (worldPos.z > UnifDirLightPass.mSplitDistance[2])
index = 2;
vec4 projCoords = UnifDirLightPass.mVPMatrix[index] * vec4(worldPos, 1.0);
projCoords.w = projCoords.z - DEPTH_BIAS;
projCoords.z = float(index);
float visibilty = texture(unifShadowTexture, projCoords);
float angleNormal = clamp(dot(normal, UnifDirLightPass.mLightDir.xyz), 0, 1);
fragColor = vec4(diffuse, 1.0) * visibilty * angleNormal * UnifDirLightPass.mLightColor;
}
Any ideas on what could be wrong? I suspect the might be some issue with the way I select the index, but I havn't figured it out yet.
EDIT: Screenshots to show the problem. The first three show that the shadow mapping works on and off at various distances, and the fourth shows the last split where it works as intended
http://s23.postimg.org/ngwk5gnff/image.png
http://s11.postimg.org/b9cqu45sz/image.png
http://s16.postimg.org/4daom7ded/image.png
http://s27.postimg.org/y7gs1kbmb/image.png
EDIT2: added more screenshots with debug
The colors show the farDistance (red, green, blue, white) http://s13.postimg.org/asvxzfe3b/image.png http://s23.postimg.org/gew8jmfnv/1_color.png¨
The splits looks okay in the above, but notice how the shadows cut out still... and no shadows on the cube?
http://s21.postimg.org/gc0tn3obr/image.png http://s28.postimg.org/xlmnxe3al/2_color.png
EDIT3:
Heres how I create the lights VP matrix used for generating shadowmap (which is then multiplied by a bias matrix before passed on to shader)
Mat4 CreateDirLightVPMatrix(const CameraFrustrum& cameraFrustrum, const Vec3& lightDir)
{
const Vec3 lightDirx = glm::normalize(lightDir);
const Vec3 perpVec1 = glm::normalize(glm::cross(lightDirx, Vec3(0.0f, 0.0f, 1.0f)));
const Vec3 perpVec2 = glm::normalize(glm::cross(lightDirx, perpVec1));
Mat4 lightViewMatrix(Vec4(perpVec1, 0.0f), Vec4(perpVec2, 0.0f), Vec4(lightDirx, 0.0f), Vec4(0.0f, 0.0f, 0.0f, 1.0f));
Vec4 transf = lightViewMatrix * cameraFrustrum[0]; // cameraFrustrum is a std::array<Vec4, 8> and 0-3 is near-points and 4-7 are far points of the frustrum
float maxZ = cameraFrustrum[0].z, minZ = cameraFrustrum[0].z;
for (uint32_t i = 1; i < 8; i++)
{
transf = lightViewMatrix * cameraFrustrum[i];
if (cameraFrustrum[i].z > maxZ)
maxZ = cameraFrustrum[i].z;
if (cameraFrustrum[i].z < minZ)
minZ = cameraFrustrum[i].z;
}
const Mat4 mvp = glm::ortho(-1.0f, 1.0f, -1.0f, 1.0f, maxZ, minZ) * lightViewMatrix;
float maxX = -1000.0f, minX = 1000.0f;
float maxY = -1000.0f, minY = 1000.0f;
for (uint32_t i = 0; i < 8; i++)
{
transf = mvp * cameraFrustrum[i];
if (cameraFrustrum[i].x > maxX)
maxX = cameraFrustrum[i].x;
if (cameraFrustrum[i].x < minX)
minX = cameraFrustrum[i].x;
if (cameraFrustrum[i].y > maxY)
maxY = cameraFrustrum[i].y;
if (cameraFrustrum[i].y < minY)
minY = cameraFrustrum[i].y;
}
float scaleX = 2.0f / (maxX - minX);
float scaleY = 2.0f / (maxY - minY);
float offsetX = -0.5f * (maxX + minX) * scaleX;
float offsetY = -0.5f * (maxY + minY) * scaleY;
Mat4 cropMatrix(1.0f);
cropMatrix[0][0] = scaleX;
cropMatrix[1][1] = scaleY;
cropMatrix[3][0] = offsetX;
cropMatrix[3][1] = offsetY;
return cropMatrix * glm::ortho(-1.0f, 1.0f, -1.0f, 1.0f, maxZ, minZ) * lightViewMatrix;
}
Any hints are much appreciated