Hi, I just implemented CSM but I found that it dose not fit 100%, I fixed this by multiplying the radius by 2.5, but I want to know that made it not fitting in the first place and if there is a better solution
Here are the results:
radius only:
with radius multiplier
#include "Graphics/ShadowMap.hpp"
#include "Core/Log.hpp"
#include "Graphics/Skybox.hpp"
#include "Renderer/RenderManager.hpp"
#include "glm/ext/matrix_float4x4.hpp"
#include "glm/ext/matrix_transform.hpp"
#include "glm/ext/matrix_clip_space.hpp"
#include "glm/ext/vector_float3.hpp"
#include "glm/geometric.hpp"
#include <cmath>
using namespace PhantomEngine;
void ShadowMap::Init(){
glGenFramebuffers(1,&ShadowmapFBO);
glGenTextures(1, &ID);
glBindTexture(GL_TEXTURE_2D_ARRAY, ID);
glTexImage3D(GL_TEXTURE_2D_ARRAY,0,GL_DEPTH_COMPONENT32F,ShadowMapReslotion,ShadowMapReslotion,int(CascadesLevels.size())+1,0,GL_DEPTH_COMPONENT,GL_FLOAT,nullptr);
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D_ARRAY,GL_TEXTURE_MAG_FILTER,GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D_ARRAY,GL_TEXTURE_WRAP_S,GL_CLAMP_TO_BORDER);
glTexParameteri(GL_TEXTURE_2D_ARRAY,GL_TEXTURE_WRAP_T,GL_CLAMP_TO_BORDER);
float clampColor[] = {1, 1, 1, 1};
glTexParameterfv(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_BORDER_COLOR, clampColor);
glBindFramebuffer(GL_FRAMEBUFFER, ShadowmapFBO);
glFramebufferTexture(GL_FRAMEBUFFER,GL_DEPTH_ATTACHMENT,ID,0);
glDrawBuffer(GL_NONE);
glReadBuffer(GL_NONE);
glBindTexture(GL_TEXTURE_2D, 0);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glGenBuffers(1,&matricesUBO);
glBindBuffer(GL_UNIFORM_BUFFER,matricesUBO);
glBufferData(GL_UNIFORM_BUFFER,sizeof(glm::mat4x4)*16,nullptr,GL_STATIC_DRAW);
glBindBufferBase(GL_UNIFORM_BUFFER,0,matricesUBO);
glBindBuffer(GL_UNIFORM_BUFFER,0);
}
void ShadowMap::Bind(){
const auto lightmatrices = GetLightSpaceMatrices();
glBindBuffer(GL_UNIFORM_BUFFER,matricesUBO);
for (size_t i =0;i<lightmatrices.size(); i++) {
glBufferSubData(GL_UNIFORM_BUFFER, i* sizeof(glm::mat4x4),sizeof(glm::mat4x4),&lightmatrices[i]);
}
glBindBuffer(GL_UNIFORM_BUFFER,0);
glEnable(GL_DEPTH_TEST);
glViewport(0,0,ShadowMapReslotion,ShadowMapReslotion);
glBindFramebuffer(GL_FRAMEBUFFER, ShadowmapFBO);
glFramebufferTexture(GL_FRAMEBUFFER,GL_TEXTURE_BINDING_2D_ARRAY,ID,0);
glClear(GL_DEPTH_BUFFER_BIT);
glCullFace(GL_FRONT);
}
glm::mat4 ShadowMap::GetLightSpaceMatrix(const float nearplane,const float farplane){
UpdatePreojection(nearplane, farplane);
Corners = GetFrustomCornersWS(Projection,RenderManager::View);
UpdateCenter();
ShadowDirection = glm::normalize(ShadowDirection);
glm::vec3 up = glm::vec3(0,1,0);
glm::mat4 ShadowView = glm::lookAt(center-ShadowDirection, center,up);
float radius = glm::length(center - glm::vec3(Corners[0]));
for (uint i = 0;i<8;i++) {
float distance = glm::length(center-glm::vec3(Corners[i]));
radius = glm::max(radius,distance);
}
radius = std::ceil(radius);
glm::vec3 minBox = center-glm::vec3(radius*2.5f);
glm::vec3 maxBox = center+glm::vec3(radius*2.5f);
minBox = glm::vec3(ShadowView*glm::vec4(minBox,1.0f));
maxBox = glm::vec3(ShadowView*glm::vec4(maxBox,1.0f));
float near = minBox.z;
float far = maxBox.z;
glm::mat4 ShadowOrtho = glm::ortho(minBox.x,maxBox.x,minBox.y,maxBox.y,near,far);
glm::mat4 ShadowMatrix = ShadowOrtho * ShadowView;
glm::vec4 ShadowOrigin = glm::vec4(0,0,0,1);
ShadowOrigin = ShadowMatrix*ShadowOrigin;
float storedW = ShadowOrigin.w;
ShadowOrigin = ShadowOrigin*(float)ShadowMapReslotion/2.0f;
glm::vec4 roundedOrigin = glm::round(ShadowOrigin);
glm::vec4 roundedOffset = roundedOrigin-ShadowOrigin;
roundedOffset = roundedOffset * 2.0f/(float)ShadowMapReslotion;
roundedOffset.z = 0;
roundedOffset.w = 0;
glm::mat4 ShadowProj = ShadowOrtho;
ShadowProj[3] +=roundedOffset;
ShadowOrtho = ShadowProj;
return ShadowOrtho * ShadowView;
}
void ShadowMap::UpdatePreojection(float near,float far){
Projection = glm::mat4(1.0f);
Projection = glm::perspective(glm::radians(FOV),Aspect, near,far);
}
void ShadowMap::ActiveTexture(){
glEnable(GL_CULL_FACE);
glCullFace(GL_BACK);
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
glActiveTexture(GL_TEXTURE0+1);
glBindTexture(GL_TEXTURE_2D_ARRAY, ID);
};
#pragma once
#include "glm/ext/matrix_float4x4.hpp"
#include "glm/ext/vector_float3.hpp"
#include "glm/ext/vector_float4.hpp"
#include <cstdint>
#include <glad/glad.h>
#include <vector>
namespace PhantomEngine {
class ShadowMap{
public:
float ShadowDistance = 250.0f;
float FOV;
float Aspect;
float NearPlane,FarPlane;
unsigned int ShadowmapFBO;
unsigned int ID;
uint16_t ShadowMapReslotion = 2048;
glm::vec3 ShadowDirection;
std::vector<float> CascadesLevels ={ShadowDistance/50.0f,ShadowDistance/25.0f,ShadowDistance/10.0f,ShadowDistance/2.0f};
ShadowMap(){}
void Init();
void Bind();
glm::mat4 GetLightSpaceMatrix(const float nearplane,const float farplane);
void ActiveTexture();
void UpdatePreojection(float near,float far);
private:
uint matricesUBO;
glm::mat4 Projection;
std::vector<glm::vec4> GetFrustomCornersWS(const glm::mat4& projection,glm::mat4& view){
const auto inversProjView = glm::inverse(projection*view);
std::vector<glm::vec4> corners;
for (unsigned int x = 0;x<2;x++) {
for (unsigned int y = 0;y<2;y++) {
for (unsigned int z = 0;z<2;z++) {
const glm::vec4 point = inversProjView*glm::vec4(2.0f*x-1.0f,2.0f*y-1.0f,2.0f*z-1.0f,1.0f);
corners.push_back(point/point.w);
}
}
}
return corners;
}
std::vector<glm::vec4> Corners;
glm::vec3 center = glm::vec3(0.0);
void UpdateCenter(){
center = glm::vec3(0.0f,0.0f,0.0f);
for (const auto& v : Corners) {
center+=glm::vec3(v);
}
center/= Corners.size();
}
std::vector<glm::mat4> GetLightSpaceMatrices(){
std::vector<glm::mat4> ret;
for (size_t i = 0;i<CascadesLevels.size(); i++) {
if(i ==0){
ret.push_back(GetLightSpaceMatrix(NearPlane,CascadesLevels[i]));
}else if(i <CascadesLevels.size()){
ret.push_back(GetLightSpaceMatrix(CascadesLevels[i-1],CascadesLevels[i]));
}else{
ret.push_back(GetLightSpaceMatrix(CascadesLevels[i-1],FarPlane));
}
}
return ret;
}
};
}

