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
相关产品推荐
相关产品推荐

