You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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^T projects 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.08 21:52:45