OpenGL阴影贴图投影自动调整失效问题排查与修复咨询
Alright, let's break down why your shadow projection isn't updating correctly and fix those issues step by step:
1. The Root Causes of Your Problems
Your current approach has two critical flaws that lead to objects moving out of the shadow camera's view and distortion when moving the light upward:
- Incorrect matrix multiplication order: You're using
tmp = v * P(vector left-multiplied by matrix) but GLM uses column-major matrices by default. This reverses the transformation and messes up the projection entirely. - Unnecessary projection to the light's near plane: Calculating
A*(A^T*A)^-1*A^Tprojects points onto the light's near plane, but you don't need this—orthographic shadow projections should directly use the object's bounds in the light's view space to set left/right/top/bottom/near/far values.
2. Fix #1: Simplify Bounding Box Extraction
If object.boundingBoxViewSpace is already in the light's view space, you can directly extract min/max values from the 8 bounding box vertices to build your orthographic projection. Here's the corrected adjustLight function:
void CGRenderer::adjustLight() { std::vector<glm::vec3> bbPoints; // Populate 8 bounding box vertices (your original code is fine here) bbPoints.push_back(glm::vec3(object.boundingBoxViewSpace[0][0], object.boundingBoxViewSpace[0][1], object.boundingBoxViewSpace[0][2])); bbPoints.push_back(glm::vec3(object.boundingBoxViewSpace[1][0], object.boundingBoxViewSpace[0][1], object.boundingBoxViewSpace[0][2])); bbPoints.push_back(glm::vec3(object.boundingBoxViewSpace[0][0], object.boundingBoxViewSpace[0][1], object.boundingBoxViewSpace[1][2])); bbPoints.push_back(glm::vec3(object.boundingBoxViewSpace[1][0], object.boundingBoxViewSpace[0][1], object.boundingBoxViewSpace[1][2])); bbPoints.push_back(glm::vec3(object.boundingBoxViewSpace[0][0], object.boundingBoxViewSpace[1][1], object.boundingBoxViewSpace[0][2])); bbPoints.push_back(glm::vec3(object.boundingBoxViewSpace[1][0], object.boundingBoxViewSpace[1][1], object.boundingBoxViewSpace[0][2])); bbPoints.push_back(glm::vec3(object.boundingBoxViewSpace[0][0], object.boundingBoxViewSpace[1][1], object.boundingBoxViewSpace[1][2])); bbPoints.push_back(glm::vec3(object.boundingBoxViewSpace[1][0], object.boundingBoxViewSpace[1][1], object.boundingBoxViewSpace[1][2])); // Initialize extents with the first point float x_min = bbPoints[0].x; float x_max = bbPoints[0].x; float y_min = bbPoints[0].y; float y_max = bbPoints[0].y; float z_min = bbPoints[0].z; float z_max = bbPoints[0].z; // Iterate to find min/max for all axes for (const glm::vec3& v : bbPoints) { x_min = std::min(x_min, v.x); x_max = std::max(x_max, v.x); y_min = std::min(y_min, v.y); y_max = std::max(y_max, v.y); z_min = std::min(z_min, v.z); z_max = std::max(z_max, v.z); } // Add a small margin to prevent clipping artifacts const float margin = 0.1f; parameter.lightprojection_x_min = x_min - margin; parameter.lightprojection_x_max = x_max + margin; parameter.lightprojection_y_min = y_min - margin; parameter.lightprojection_y_max = y_max + margin; parameter.lightprojection_z_min = z_min - margin; parameter.lightprojection_z_max = z_max + margin; // Build orthographic projection (GLM order: left, right, bottom, top, near, far) lightsource_projection = glm::ortho( parameter.lightprojection_x_min, parameter.lightprojection_x_max, parameter.lightprojection_y_min, parameter.lightprojection_y_max, parameter.lightprojection_z_min, parameter.lightprojection_z_max ); }
3. Fix #2: Ensure Bounding Box is in the Correct Space
If object.boundingBoxViewSpace is actually in world space (not light view space), you need to transform each vertex into the light's view space first. Add this inside adjustLight before calculating extents:
// Get light's view matrix glm::mat4 shadow_view = lightsource_camera.getViewMatrix(); // Transform world space bounding box points to light view space for (glm::vec3& v : bbPoints) { v = glm::vec3(shadow_view * glm::vec4(v, 1.0f)); }
And if your bounding box is in local object space, multiply by the model matrix first:
v = glm::vec3(shadow_view * model * glm::vec4(v, 1.0f));
4. Fix #3: Reorder Shadow Pass Execution
Your shadowmap_pass calculates shadow_model_view before calling adjustLight, which means the bounding box might not reflect the latest object/light state. Reorder the steps:
void CGRenderer::shadowmap_pass() { glBindFramebuffer(GL_FRAMEBUFFER, framebuffers.shadowmap_buffer); glViewport(0, 0, shadowmap_width, shadowmap_height); glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT); // Calculate model matrix first model = glm::scale(glm::mat4_cast(parameter.globalRotation), glm::vec3(parameter.modelScalation)); model = glm::rotate(glm::mat4(1.f), glm::radians(parameter.modelRotation), glm::vec3(0.f, 1.f, 0.f)) * model; // Get light view matrix glm::mat4 shadow_view = lightsource_camera.getViewMatrix(); // Update bounding box to light view space (if needed) // ... (add the transformation code here if your bounding box isn't already in view space) // Adjust light projection based on updated bounding box adjustLight(); // Calculate final MVP glm::mat4 shadow_model_view = shadow_view * model; glm::mat4 mvp = lightsource_projection * shadow_model_view; shader->use(); glUniformMatrix4fv(locs.modelViewProjection, 1, GL_FALSE, glm::value_ptr(mvp)); glUniformSubroutinesuiv(GL_VERTEX_SHADER, 1, &locs.placementVS); glUniformSubroutinesuiv(GL_FRAGMENT_SHADER, 1, &locs.depthmapFS); glBindVertexArray(basesurface.vao); glDrawArrays(GL_TRIANGLES, 0, basesurface.vertsToDraw); glBindVertexArray(object.vao); glDrawArrays(GL_TRIANGLES, 0, object.vertsToDraw); }
5. Do You Need to Modify the View Matrix?
No, you don't need to change the light's view matrix. The issue was never with the view matrix—it was with how you calculated the orthographic projection bounds. By using the object's actual extents in the light's view space, you'll ensure the object stays in the shadow camera's view regardless of light position.
Quick Debug Tip
To verify your shadow map is working correctly, temporarily render the shadow map as a texture on a quad in your main view. This lets you visually check if the object is fully contained within the shadow map's bounds.
内容的提问来源于stack exchange,提问作者MolineraNegra

