【问题标题】:Automatize adjustment of the Shadow Map Projection (by projection the points of interest onto the near clipping plane of the light source)自动调整阴影贴图投影(通过将兴趣点投影到光源的近剪裁平面上)
【发布时间】:2026-02-03 01:40:01
【问题描述】:

我正在使用 OpenGL,所以使用 C++ 进行编码。

任务: 我有一个简单的场景,其中有一个对象、一个单点光源和一个相机。现在我在物体周围移动灯光并向上移动。当我到达我想要的所有位置时,我会旋转对象和/或向上移动相机。当我得到光和相机的所有位置时,我切换对象。 当然,照明和阴影始终应该是正确的。但目前,情况并非如此。这导致我...

问题: 有时背景中有阴影,实际上不应该是一个 image with the wrong shadow in the back

我希望阴影贴图不够大,无法捕捉到这部分场景。由于场景中的多次变化,我想自动定义阴影贴图的大小。

我目前所拥有的: 我在每个对象周围定义了最大边界框,以了解此处的最大尺寸。通过绘制边界框和调试我可以说这是计算正确的。 (我知道我也必须考虑基础表面的范围,但为了澄清我的算法是否有效,我暂时搁置了)

为了获得近剪裁平面上的范围,我将它们投影到光源的近剪裁平面上,以获得我需要使用的阴影贴图的尺寸。对于投影,我跨越平面的底部,向上和平面的右向量。我用A * (A^T * A)^(-1) * A^T 计算的投影矩阵 当我现在切换视图以便从灯光的角度看到场景时,我看到了我所期望的:整个画面就是对象。

object from the light perspective

到目前为止一切顺利。但是当我将灯光向右或向左移动时,图像的一部分会移出视野。

light moved to the right

当我将灯移开时,物体会被锻造。

light moved up

这是我调整 lightsource_projection 的代码:

void CGRenderer::adjustLight()
    {
        glm::mat2x3 A(9.0f);
        A[1] = lightsource_camera.getUp();
        A[0] = lightsource_camera.getRight();

        //projection matrix = A(ATA)^-1 AT.
        glm::mat3 P = A * glm::inverse(glm::transpose(A) * A) * glm::transpose(A);
std::vector<glm::vec3> bbPoints;
        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]));

        glm::vec3 tmp;
        float x_min = 0.f;
        float x_max = 0.f;
        float y_min = 0.f;
        float y_max = 0.f;
        for (glm::vec3 v : bbPoints)
        {
            tmp = v * P;

            if (tmp.x < x_min)
                x_min = tmp.x;
            if (tmp.x > x_max)
                x_max = tmp.x;
            if (tmp.y < y_min)
                y_min = tmp.y;
            if (tmp.y > y_max)
                y_max = tmp.y;
        }

        parameter.lightprojection_x_min = x_min;
        parameter.lightprojection_x_max = x_max;
        parameter.lightprojection_y_min = y_min;
        parameter.lightprojection_y_max = y_max;

        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);
}

这是我调整光线并使用新计算的 lightsource_projection 的阴影传递函数:

    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);

    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;

    glm::mat4 shadow_view = lightsource_camera.getViewMatrix();

    glm::mat4 shadow_model_view = shadow_view * model;


    adjustLight();

    shader->use();
    glUniformMatrix4fv(locs.modelViewProjection, 1, GL_FALSE, glm::value_ptr(lightsource_projection * shadow_model_view));

    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);
}

问题: 为什么它没有正确更新投影?我该如何解决这个问题?我需要改变视图矩阵来解决问题吗?

【问题讨论】:

    标签: c++ opengl projection glm-math shadow-mapping


    【解决方案1】:

    这是我的解决方案:

    我将边界顶点转换为光照视图空间。然后投影它。通过使用归一化的正交投影矩阵,我可以直接得到我想要的值。

        void adjustLight(glm::mat4 shadow_model_view)
    {
    glm::vec4 tmp;
    float x_min = 0.f;
    float x_max = 0.f;
    float y_min = 0.f;
    float y_max = 0.f;
    for (glm::vec3 v : object.boundingVertices) // before bbPoints
    {
        tmp = shadow_model_view * glm::vec4(v, 1.f) ;
        tmp = glm::ortho(-1.f, 1.f, -1.f, 1.f, -1.f, 1.f) * tmp;
    
        if (tmp.x < x_min)
            x_min = tmp.x;
        if (tmp.x > x_max)
            x_max = tmp.x;
        if (tmp.y < y_min)
            y_min = tmp.y;
        if (tmp.y > y_max)
            y_max = tmp.y;
    }
            // z_min und z_max are predefined
    lightsource_projection = glm::ortho(x_min, x_max, y_min, y_max, z_min, z_max);
    }
    

    【讨论】: