How to calculate the shadow bounding box with a directional light

I'm trying to implement a directional light in my C++ / OpenGL engine, but I struggle to calculate the shadow bounding box (i.e the shadow frustum). The bounding box has to encompass the main frustum and be aligned with the light rays, resulting in an orhographic frustum.

The problem is that as the light have no position, I don't know how to create the light view matrix, which is necessary to calculate the aligned bounding box.

Here's my current attempt:

• calculation of the main frustum 8 coordinates in world space
• conversion of these 8 coordinates in light view space, using the lightView matrix
• calculation of a bounding box around the main frustum, aligned with light rays since I'm in light view space
• calculation of the bounding box center, in light view space
• conversion of the bounding box center in world space (using the inverse of the lightView matrix)
• calculation of the bounding box projection matrix with glm::ortho
• calculation of the bounding box view matrix (the lightView matrix) with glm::lookAt. For this I need the bounding box center, which I calculated... using the lightView matrix.

This doesn't work, because I need the lightView matrix to calculate the bounding box center. But I need the bounding box center to calculate the lightView matrix.

I can't manage to get it right. What am I missing?

Here is the code:

{

// Here we convert main camera frustum coordinates in light view space
std::array<glm::vec3,8> points = {
// Near plane points
lightView * glm::vec4(cameraPtr->ntl, 1.0),
lightView * glm::vec4(cameraPtr->ntr, 1.0),
lightView * glm::vec4(cameraPtr->nbl, 1.0),
lightView * glm::vec4(cameraPtr->nbr, 1.0),
// Far plane points
lightView * glm::vec4(cameraPtr->ftl, 1.0),
lightView * glm::vec4(cameraPtr->ftr, 1.0),
lightView * glm::vec4(cameraPtr->fbl, 1.0),
lightView * glm::vec4(cameraPtr->fbr, 1.0)};

// Here we find the shadow bounding box dimensions
bool first = true;
for (int i=0; i<7; ++i)
{
glm::vec3* point = &points[i];

if (first)
{
minX = point->x;
maxX = point->x;
minY = point->y;
maxY = point->y;
minZ = point->z;
maxZ = point->z;
first = false;
}

if (point->x > maxX)
maxX = point->x;
else if (point->x < minX)
minX = point->x;

if (point->y > maxY)
maxY = point->y;
else if (point->y < minY)
minY = point->y;

if (point->z > maxZ)
maxZ = point->z;
else if (point->z < minZ)
minZ = point->z;
}

frustumWidth = maxX - minX;
frustumHeight = maxY - minY;
frustumLength = maxZ - minZ;

// Here we find the bounding box center, in light view space
float x = (minX + maxX) / 2.0f;
float y = (minY + maxY) / 2.0f;
float z = (minZ + maxZ) / 2.0f;
glm::vec4 frustumCenter = glm::vec4(x, y, z, 1.0f);

// Here we convert the bounding box center in world space
glm::mat4 invertedLight = glm::mat4(1.0f);
invertedLight = glm::inverse(lightView);
frustumCenter = invertedLight * frustumCenter;

// Here we define the light projection matrix (shadow frustum dimensions)
lightProjection = glm::ortho(
-frustumWidth/2.0f, // left
frustumWidth/2.0f, // right
-frustumHeight/2.0f, // down
frustumHeight/2.0f, // top
0.01f, // near

// Here we define the light view matrix (shadow frustum position and orientation)
lightDirection = glm::normalize(lightDirection);
target = glm::vec3(frustumCenter) + lightDirection;

lightView = glm::lookAt(
glm::vec3(frustumCenter),

// Light orientation
target,

// Up vector
glm::vec3( 0.0f, 1.0f,  0.0f));

// Final matrix calculation
lightSpaceMatrix = lightProjection * lightView;
}
• The only part of the LightView matrix that the bounding box center should affect is its translation, which doesn't change anything about the light. So couldn't you use an arbitrary translation (say, the translation from the previous frame, or to the player, or to the center of the view frustum) for your first step, then use the result to calculate a revised matrix with a new translation for the later step? Aug 28 '21 at 14:04
• Thank you a lot for your help DMGregory. It helped me to find the solution, which I posted below. Have a good day. Aug 29 '21 at 10:11

Well, probably not the best way but this works pretty well.

Before to process the shadow bounding box, calculate a lightView matrix based on the main frustum center, with the light view set to a position relative to this center.

lightView = glm::lookAt(
// Eye
// Target