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

OpenGL中深度图矩阵计算错误,阴影映射实现遇阻

阴影映射矩阵计算异常排查

问题现象

在PyOpenGL项目中实现阴影映射时,矩阵计算出现异常:

  • 代表“太阳”的光照视角始终固定在世界坐标(0, 0, 0)处,渲染距离对应0到1的黑白深度值
  • 移除mvp_matrix = projection_view_matrix * model_matrix的乘法操作,仅将model_matrix传入着色器,输出结果完全一致,说明矩阵乘法环节存在问题

矩阵创建代码

光照视图矩阵生成代码

def update_light_view_matrix(self, direction: vec3, center: vec3) -> None:
    """
    更新光照的视图矩阵。该矩阵会让"视锥体"的方向与光照方向对齐。
    光照本身没有位置,因此视图矩阵以"视锥体"的中心为原点。
    生成的视图矩阵决定了"视锥体"在世界空间中的位置和朝向,而视锥体的尺寸由投影矩阵决定。
    :param direction: 光照方向,即视锥体需要指向的方向
    :param center: 视锥体在世界空间中的中心坐标
    :return:
    """
    direction = direction.normalize()
    self.__light_view_matrix = mat4().identity()
    # 计算俯仰角
    pitch = math.acos(math.sqrt(direction.x**2 + direction.z**2))
    self.__light_view_matrix = self.__light_view_matrix.rotate(pitch, vec3(1, 0, 0))
    # 计算偏航角
    yaw = math.degrees(math.atan(direction.x / direction.z))
    yaw = yaw - 180 if direction.z > 0 else yaw
    self.__light_view_matrix = self.__light_view_matrix.rotate(-math.radians(yaw), vec3(0, 1, 0))
    # 平移到视锥体中心
    self.__light_view_matrix = self.__light_view_matrix.translate(-center)

正交投影矩阵生成代码

def update_ortho_projection_matrix(self, width: float, height: float, length: float) -> None:
    """
    创建正交投影矩阵。该矩阵根据ShadowBox类计算出的值,设置视锥体的宽、高、长。
    :param width: 阴影盒宽度
    :param height: 阴影盒高度
    :param length: 阴影盒长度
    :return:
    """
    self.__projection_matrix = mat4()
    self.__projection_matrix[(0, 0)] = 2.0 / width
    self.__projection_matrix[(1, 1)] = 2.0 / height
    self.__projection_matrix[(2, 2)] = -2.0 / length
    self.__projection_matrix[(3, 3)] = 1

MVP矩阵计算代码

...
self.__projection_view_matrix = self.__projection_matrix * self.__light_view_matrix
...
mvp_matrix = self.__projection_view_matrix * model_matrix

矩阵计算结果(供验证)

projection_matrix:
[   0.0036,    0.0000,    0.0000,    0.0000]
[   0.0000,    0.0069,    0.0000,    0.0000]
[   0.0000,    0.0000,   -0.0128,    0.0000]
[   0.0000,    0.0000,    0.0000,    1.0000]

light_view_matrix:
[  -0.7071,    0.0000,   -0.7071,  -15.1670]
[  -0.5145,    0.6860,    0.5145,   22.6592]
[   0.4851,    0.7276,   -0.4851,    1.9242]
[   0.0000,    0.0000,    0.0000,    1.0000]

projection_view_matrix:
[  -0.0026,    0.0000,   -0.0026,   -0.0552]
[  -0.0035,    0.0047,    0.0035,    0.1560]
[  -0.0062,   -0.0093,    0.0062,   -0.0245]
[   0.0000,    0.0000,    0.0000,    1.0000]

mvp_matrix:
[   1.0000,    0.0000,    0.0000,  601.0000]
[   0.0000,    1.0000,    0.0000,   -1.5510]
[   0.0000,    0.0000,    1.0000,  778.0000]
[   0.0000,    0.0000,    0.0000,    1.0000]

问题分析与解决方案

从输出的mvp_matrix可以明显看出异常:它变成了仅包含平移分量的矩阵,完全丢失了投影和视图矩阵的变换信息。核心问题是矩阵乘法顺序搞反了。

OpenGL使用列主序矩阵,坐标空间转换顺序为「模型空间→世界空间→视图空间→裁剪空间」,对应的矩阵乘法顺序应为:

mvp_matrix = model_matrix * self.__light_view_matrix * self.__projection_matrix

或基于已预计算的projection_view_matrix,修正为:

mvp_matrix = model_matrix * self.__projection_view_matrix

你当前的写法是投影视图矩阵左乘模型矩阵,相当于把模型变换应用在了投影变换之后,完全不符合坐标转换逻辑,导致变换失效。

另外,优化光照视图矩阵的偏航角计算:
原代码中math.atan(direction.x / direction.z)存在direction.z=0的除零风险,改用math.atan2可避免该问题,同时简化角度调整逻辑:

yaw = math.degrees(math.atan2(direction.x, direction.z))

无需额外判断direction.z的正负来调整角度。

修正乘法顺序并优化角度计算后,重新验证矩阵结果,即可得到正确的MVP矩阵,阴影映射的视角会正常跟随光照方向和视锥体中心。


内容的提问来源于stack exchange,提问作者Andreas Sabelfeld

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.15 10:14:56