i'm trying to implement Cascaded Shadow mapping using opengl but i got some problems.
i start by dividing my view frustum to three splits and for each split it has a
1- near
2- far
3- corners (corners of the frustum of this specific split in world space)
4- depth map (2D Texture with dimensions of 1024 * 1024)
and for each split i start with calculating its corners as follows and with these corners in world space i calculate frustum center that i will be using to calculate light view matrix.
float width = float(mRenderer->GetGame()->GetWidth());
float height = float(mRenderer->GetGame()->GetHeight());
mProjMatrix = glm::perspective(glm::radians(90.0f), (float)width / (float)height, mNear, mFar);
mViewMatrix = mRenderer->GetView();
glm::mat4 viewProj = mProjMatrix * mViewMatrix;
glm::vec3 frustumCorners[8] =
{
glm::vec3(-1.0f, 1.0f, -1.0f),
glm::vec3(1.0f, 1.0f, -1.0f),
glm::vec3(1.0f, -1.0f, -1.0f),
glm::vec3(-1.0f, -1.0f, -1.0f),
glm::vec3(-1.0f, 1.0f, 1.0f),
glm::vec3(1.0f, 1.0f, 1.0f),
glm::vec3(1.0f, -1.0f, 1.0f),
glm::vec3(-1.0f, -1.0f, 1.0f),
};
for (int i = 0; i < 8; ++i)
{
glm::vec4 inversePoint = glm::inverse(viewProj) * glm::vec4(frustumCorners[i], 1.0f);
mCorners[i] = glm::vec3(inversePoint / inversePoint.w);
}
for (int i = 0; i < 8; ++i)
{
mFrustumCenter += mCorners[i];
}
mFrustumCenter /= 8.0f;
after i have the frustum center of this specific split i need to figure out the light view matrix that i will be using to render the scene (between near and far of the split) and i do that as follows.
mRenderer->GetLightDirection() = {0.0f, 20.0f, -1.0f}
glm::vec3 lightDir = glm::normalize(mRenderer->GetLightDirection());
glm::vec3 lightPos = mFrustumCenter + lightDir;
mLightView = glm::lookAt(lightPos, mFrustumCenter , glm::vec3(0.0f, 1.0f, 0.0f));
and finally the last thing i do is calculating the orthographic matrix of the light using the split frustum corners after i transform them into light space with the light view matrix i calculated the previous step.
for (int i = 0; i < 8; ++i)
{
mCorners[i] = glm::vec3(mLightView * glm::vec4(mCorners[i], 1.0f));
}
float minX = std::numeric_limits<float>::max();
float maxX = std::numeric_limits<float>::min();
float minY = std::numeric_limits<float>::max();
float maxY = std::numeric_limits<float>::min();
float minZ = std::numeric_limits<float>::max();
float maxZ = std::numeric_limits<float>::min();
for (int i = 0; i < 8; ++i)
{
minX = std::min(minX, mCorners[i].x);
maxX = std::max(maxX, mCorners[i].x);
minY = std::min(minY, mCorners[i].y);
maxY = std::max(maxY, mCorners[i].y);
minZ = std::min(minZ, mCorners[i].z);
maxZ = std::max(maxZ, mCorners[i].z);
}
mLightProj = glm::ortho(minX, maxX, minY, maxY, minZ, maxZ);
when i run my program i have my shadow working correctly
but when i move the camera back until the floor enter the range of the second split instead of using the second split shadow it disappears but when i start to move the camera up and down the shadow appears again so i think the problem is in calculating light view matrix but i couldn't figure it out.
these are the ranges of my splits
near-> far
0.1 -> 30.0
0.1 -> 50.0
0.1 -> 1000.0
well i figured it out i wasn't placing the light correctly so this snippet
glm::vec3 lightDir = glm::normalize(mRenderer->GetLightDirection());
glm::vec3 lightPos = mFrustumCenter + lightDir;
mLightView = glm::lookAt(lightPos, mFrustumCenter , glm::vec3(0.0f, 1.0f, 0.0f));
should be
glm::vec3 lightDir = glm::normalize(mRenderer->GetLightDirection());
glm::vec3 lightPos = mFrustumCenter + lightDir * (mFar - mNear);
mLightView = glm::lookAt(lightPos, mFrustumCenter , glm::vec3(0.0f, 1.0f, 0.0f));
i should be moving the light position in the the other direction an amount equal to the difference between the near and the far of the current split frustum.
and also this snippet
float minX = std::numeric_limits<float>::max();
float maxX = std::numeric_limits<float>::min();
float minY = std::numeric_limits<float>::max();
float maxY = std::numeric_limits<float>::min();
float minZ = std::numeric_limits<float>::max();
float maxZ = std::numeric_limits<float>::min();
for (int i = 0; i < 8; ++i)
{
minX = std::min(minX, mCorners[i].x);
maxX = std::max(maxX, mCorners[i].x);
minY = std::min(minY, mCorners[i].y);
maxY = std::max(maxY, mCorners[i].y);
minZ = std::min(minZ, mCorners[i].z);
maxZ = std::max(maxZ, mCorners[i].z);
}
mLightProj = glm::ortho(minX, maxX, minY, maxY, minZ, maxZ);
should be
float minX = std::numeric_limits<float>::max();
float maxX = std::numeric_limits<float>::min();
float minY = std::numeric_limits<float>::max();
float maxY = std::numeric_limits<float>::min();
float minZ = std::numeric_limits<float>::max();
float maxZ = std::numeric_limits<float>::min();
for (int i = 0; i < 8; ++i)
{
minX = std::min(minX, mCorners[i].x);
maxX = std::max(maxX, mCorners[i].x);
minY = std::min(minY, mCorners[i].y);
maxY = std::max(maxY, mCorners[i].y);
minZ = std::min(minZ, mCorners[i].z);
maxZ = std::max(maxZ, mCorners[i].z);
}
mLightProj = glm::ortho(minX, maxX, minY, maxY, near, maxZ - minZ);
the near and of the light orthographic projection matrix should be equal to the near of the split frustum and the far should be equal to the distance between the closest corner and the furthest corner (z axis)