Good day !
I faced with problem connected with Shadow Map slices.
I took algorithm from article
Matrix Light::CalculateCropMatrix(Frustum splitFrustum)
{
Matrix lightViewProjMatrix = viewMatrix * projMatrix;
// Find boundaries in light's clip space
BoundingBox cropBB = CreateAABB(splitFrustum.AABB,
lightViewProjMatrix);
// Use default near-plane value
cropBB.min.z = 0.0f;
// Create the crop matrix
float scaleX, scaleY, scaleZ;
float offsetX, offsetY, offsetZ;
scaleX = 2.0f / (cropBB.max.x - cropBB.min.x);
scaleY = 2.0f / (cropBB.max.y - cropBB.min.y);
offsetX = -0.5f * (cropBB.max.x + cropBB.min.x) * scaleX;
offsetY = -0.5f * (cropBB.max.y + cropBB.min.y) * scaleY;
scaleZ = 1.0f / (cropBB.max.z - cropBB.min.z);
offsetZ = -cropBB.min.z * scaleZ;
return Matrix( scaleX, 0.0f, 0.0f, 0.0f,
0.0f, scaleY, 0.0f, 0.0f,
0.0f, 0.0f, scaleZ, 0.0f,
offsetX, offsetY, offsetZ, 1.0f);
}
algorithm does next things:
- calculate frustum points for each split
- all 8 pointers multiply on lightViewProjMatrix to translate each point to light clip space
- finding AABB
- building crop matrix
i used this code in my program. but shadow maps for each slice are wrong (i will upload images with shadow maps soon).
my lightView matrix is lookat matrix with parameters:
scene.lightCamera.buildViewMatf(-600, 1000, -500, 0, 0, 0, 0, 1, 0);
void buildViewMatf(float eye_x, float eye_y, float eye_z,
float earth_x, float earth_y, float earth_z,
float up_x, float up_y, float up_z)
{
m_currEyePos[0] = eye_x; m_currEyePos[1] = eye_y; m_currEyePos[2] = eye_z;
m_earthPos[0] = earth_x; m_earthPos[1] = earth_y; m_earthPos[2] = earth_z;
m_up[0] = up_x; m_up[1] = up_y; m_up[2] = up_z;
NvBuildLookatMatf(m_view, m_currEyePos, m_earthPos, m_up);
}
and i have light ortho projection matrix with parameters:
m_znear = 2.0f, m_zfar = 10000f;
NvBuildOrthoMatf(m_projection, -5000.0, 5000.0,
-5000.0, 5000.0,
m_znear, m_zfar);
finding frustum points:
void updateFrustrumPoints(float z_near, float z_far)
{
vec3f up(m_up[0], m_up[1], m_up[2]);
vec3f camera_position(m_currEyePos[0], m_currEyePos[1], m_currEyePos[2]);
vec3f view_direction(m_earthPos[0]-m_currEyePos[0],
m_earthPos[1]-m_currEyePos[1],
m_earthPos[2]-m_currEyePos[2]);
view_direction = normalize(view_direction);
vec3f right = normalize(cross(view_direction, up));
up = normalize(cross(right, view_direction));
float angle = (float) tan(45.0 * M_PI / 360.0);
float near_height = z_near * angle;
float far_height = z_far * angle;
float near_width = (1.0f / m_aspectRatio) * near_height;
float far_width = (1.0f / m_aspectRatio) * far_height;
vec3f fc = camera_position + (view_direction * z_far);
m_frustrumPoints.resize(8, vec3f(0.0, 0.0, 0.0));
// far-top-left
m_frustrumPoints[0] = fc + up*far_height - right*far_width;
// far-top-right
m_frustrumPoints[1] = fc + up*far_height + right*far_width;
// far-bottom-left
m_frustrumPoints[2] = fc - up*far_height - right*far_width;
// far-bottom-right
m_frustrumPoints[3] = fc - up*far_height + right*far_width;
vec3f nc = camera_position + (view_direction * z_near);
// near-top-left
m_frustrumPoints[4] = nc + up*near_height - right*near_width;
// near-top-right
m_frustrumPoints[5] = nc + up*near_height + right*near_width;
// near-bottom-left
m_frustrumPoints[6] = nc - up*near_height - right*near_width;
//near-bottom-right
m_frustrumPoints[7] = nc - up*near_height + right*near_width;
}
calculate crop matrix:
void buildCropMatrix(vector<vec3f>& _frustumPoints, int number_of_split){
vector<vec3f> frustumPoints(8, vec3f(0.0, 0.0, 0.0));
for(int i = 0; i < 8; i++) {
NvTransformPointf((GLfloat*) &frustumPoints[i], m_model_view_projection, (GLfloat*) &_frustumPoints[i]);
}
/* Find minX, maxX, minY, maxY, minZ, maxZ */
float minX = frustumPoints[0].x, maxX = frustumPoints[0].x;
float minY = frustumPoints[0].y, maxY = frustumPoints[0].y;
float minZ = frustumPoints[0].z, maxZ = frustumPoints[0].z;
for(int i = 1; i < 8; i++){
if(frustumPoints[i].x < minX) minX = frustumPoints[i].x;
if(frustumPoints[i].x > maxX) maxX = frustumPoints[i].x;
if(frustumPoints[i].y < minY) minY = frustumPoints[i].y;
if(frustumPoints[i].y > maxY) maxY = frustumPoints[i].y;
if(frustumPoints[i].z < minZ) minZ = frustumPoints[i].z;
if(frustumPoints[i].z > maxZ) maxZ = frustumPoints[i].z;
}
float scaleX = 2.0f / (maxX - minX);
m_CropMatrix[number_of_split][0][0] = scaleX;
m_CropMatrix[number_of_split][0][1] = 0.0f;
m_CropMatrix[number_of_split][0][2] = 0.0f;
m_CropMatrix[number_of_split][0][3] = 0.0f;
float scaleY = 2.0f / (maxY - minY);
float scaleZ = 1.0f / (maxZ - minZ);
m_CropMatrix[number_of_split][1][0] = 0.0f;
m_CropMatrix[number_of_split][1][1] = scaleY;
m_CropMatrix[number_of_split][1][2] = 0.0f;
m_CropMatrix[number_of_split][1][3] = 0.0f;
m_CropMatrix[number_of_split][2][0] = 0.0f;
m_CropMatrix[number_of_split][2][1] = 0.0f;
m_CropMatrix[number_of_split][2][2] = scaleZ;
m_CropMatrix[number_of_split][2][3] = 0.0f;
m_CropMatrix[number_of_split][3][0] = -0.5f * (maxX + minX) * scaleX;
m_CropMatrix[number_of_split][3][1] = -0.5f * (maxY + minY) * scaleY;
m_CropMatrix[number_of_split][3][2] = -minZ * scaleZ;
m_CropMatrix[number_of_split][3][3] = 1.0f;
}
Also i found algorithm
with code
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;
}
- Does the first method correct ?
- And what the difference (mathematical) between those two methods?