So, I've mostly gotting everything working except for one minor detail that I can't seem to get rid off, the AABB doesn't seem big enough to cover the entire frustum and it gets a bit warped whenever I rotate the camera. I've just padded the xy with an offset until I got it to go away but that wastes too much resolution. Here's when I look down the negative z axis:
And when I rotate it a bit:
Then it goes away complete as I'm perpendicular to the z axis, and if I keep going the edges are on the other side of the screen instead
I have no clue why that happens and I havent seen anyone mentioning that you need to widen the X/Y radius to get it to go away, so I'm assuming I'm doing something wrong somewhere. Here's the complete code as of now:
void CascadedShadowTechnique::renderToShadowMap(Mesh* mesh, Mesh* secondMesh)
{
//Get the active cameras frustum parameters
Frustum cameraFrustum = CameraMan.getActiveCamera()->mFrustum;
//Start off by calculating the split distances
GLfloat cascadeSplits[MAX_SPLITS] = {};
//Between 0 and 1
GLfloat lambda = 1.0f;
//min 0 max 1
GLfloat minDistance = 0.3f;
GLfloat maxDistance = 1.0f;
GLfloat nearClip = cameraFrustum.mNear;
GLfloat farClip = cameraFrustum.mFar;
GLfloat clipRange = farClip - nearClip;
//Change min/maxDistance to have get different sized splits
GLfloat minZ = nearClip + minDistance * clipRange;
GLfloat maxZ = nearClip + maxDistance * clipRange;
GLfloat range = maxZ - minZ;
GLfloat ratio = maxZ / minZ;
for (unsigned int i = 0; i < MAX_SPLITS; ++i)
{
GLfloat p = (i + 1) / static_cast<GLfloat>(MAX_SPLITS);
// n(f/n)^i/m
GLfloat log = minZ * std::pow(ratio, p);
GLfloat uniform = minZ + range * p;
GLfloat d = lambda * (log - uniform) + uniform;
cascadeSplits[i] = (d - nearClip) / clipRange;
}
for (unsigned int cascadeIterator = 0; cascadeIterator < MAX_SPLITS; ++cascadeIterator)
{
GLfloat prevSplitDistance = cascadeIterator == 0 ? minDistance : cascadeSplits[cascadeIterator - 1];
GLfloat splitDistance = cascadeSplits[cascadeIterator];
// Get the 8 points of the view frustum in world space
glm::vec3 frustumCornersWS[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),
};
glm::mat4 invViewProj = glm::inverse(CameraMan.getActiveCamera()->mProjectionMatrix * CameraMan.getActiveCamera()->getViewMatrix());
for (unsigned int i = 0; i < 8; ++i)
{
glm::vec4 inversePoint = invViewProj * glm::vec4(frustumCornersWS[i], 1.0f);
frustumCornersWS[i] = glm::vec3(inversePoint/inversePoint.w);
}
// Get the corners of the current cascade slice of the view frustum
for (unsigned int i = 0; i < 4; ++i)
{
glm::vec3 cornerRay = frustumCornersWS[i + 4] - frustumCornersWS[i];
glm::vec3 nearCornerRay = cornerRay * prevSplitDistance;
glm::vec3 farCornerRay = cornerRay * splitDistance;
frustumCornersWS[i + 4] = frustumCornersWS[i] + farCornerRay;
frustumCornersWS[i] = frustumCornersWS[i] + nearCornerRay;
}
//Calculate the frustum center
glm::vec3 frustumCenter = glm::vec3(0.0f);
for (unsigned int i = 0; i < 8; ++i)
frustumCenter += frustumCornersWS[i];
frustumCenter /= 8.0f;
GLfloat far = -INFINITY;
GLfloat near = INFINITY;
//Get the longest radius in world space
GLfloat radius = 0.0f;
for (unsigned int i = 0; i < 8; ++i)
{
GLfloat distance = glm::length(frustumCornersWS[i] - frustumCenter);
radius = glm::max(radius, distance);
}
radius = std::ceil(radius);
//Create the AABB from the radius
glm::vec3 maxOrtho = frustumCenter + glm::vec3(radius, radius, radius);
glm::vec3 minOrtho = frustumCenter - glm::vec3(radius, radius, radius);
//Just checking when debugging to make sure the AABB is the same size
GLfloat lengthofTemp = glm::length(maxOrtho - minOrtho);
GLfloat testing = radius*2.0f;
//Calculate the viewMatrix from the frustum center and light direction
glm::vec3 lightDirection = frustumCenter - glm::normalize(glm::vec3(-0.447213620f, -0.89442790f, 0.0f));
lightViewMatrix = glm::lookAt(lightDirection, frustumCenter, glm::vec3(0.0f, 1.0f, 0.0f));
//Get the AABB in light view space
maxOrtho = glm::vec3(lightViewMatrix*glm::vec4(maxOrtho, 1.0f));
minOrtho = glm::vec3(lightViewMatrix*glm::vec4(minOrtho, 1.0f));
//Store the far and near planes
far = maxOrtho.z;
near = minOrtho.z;
lightOrthoMatrix = glm::ortho(maxOrtho.x, minOrtho.x, maxOrtho.y, minOrtho.y, near, far);
// Create the rounding matrix, by projecting the world-space origin and determining
// the fractional offset in texel space
glm::mat4 shadowMatrix = lightOrthoMatrix * lightViewMatrix;
glm::vec4 shadowOrigin = glm::vec4(0.0f, 0.0f, 0.0f, 1.0f);
shadowOrigin = shadowMatrix * shadowOrigin;
shadowOrigin = shadowOrigin * mShadowMapSize / 2.0f;
glm::vec4 roundedOrigin = glm::round(shadowOrigin);
glm::vec4 roundOffset = roundedOrigin - shadowOrigin;
roundOffset = roundOffset * 2.0f / mShadowMapSize;
roundOffset.z = 0.0f;
roundOffset.w = 0.0f;
glm::mat4 shadowProj = lightOrthoMatrix;
shadowProj[3] += roundOffset;
lightOrthoMatrix = shadowProj;
// Store the split distance in terms of view space depth
const float clipDist = cameraFrustum.mFar - cameraFrustum.mNear;
cascadeSplitArray[cascadeIterator] = cameraFrustum.mNear + splitDistance * clipDist;
cascadedMatrices[cascadeIterator] = lightOrthoMatrix * lightViewMatrix;
//Now finally render the scene into FBO
ShaderMan.bindShader(SHADOW_DEPTH_PASS_SHADER);
glBindFramebuffer(GL_FRAMEBUFFER, mCascadedShadowFBO);
glViewport(0, 0, mShadowMapSize, mShadowMapSize);
glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, mCascadedTextureArray, 0, cascadeIterator);
glClear(GL_DEPTH_BUFFER_BIT);
glEnable(GL_DEPTH_TEST);
glEnable(GL_DEPTH_CLAMP);
glCullFace(GL_FRONT);
glm::mat4 lightViewProjection = lightOrthoMatrix * lightViewMatrix;
glUniformMatrix4fv(ShaderMan.getActiveShader().getUniformLocation("lightViewProjectionMatrix"), 1, GL_FALSE, &lightViewProjection[0][0]);
mesh->renderDepth();
secondMesh->renderDepth();
glBindFramebuffer(GL_FRAMEBUFFER, 0);
ShaderMan.unbindShader();
glDisable(GL_DEPTH_CLAMP);
}
}
I'm pretty confident that the frustums are calculated correctly, but I can't find any reason for what's happening.