为何逆视口投影矩阵中两个元素始终为0?
视口-投影-视图逆矩阵中e41、e42恒为0的原因与优化安全性解析
问题背景
我在编写光线追踪器时,需要通过视口-投影-视图矩阵的逆矩阵将屏幕像素坐标转换为世界空间坐标。过程中发现,无论如何移动、缩放、环绕或旋转相机,逆矩阵的最后一行前两个元素(e41、e42)始终为0。虽然利用这个特性可以提前计算w除法来优化transform方法,但不确定这个假设的成立条件,同时也疑惑为何透视矩阵的逆矩阵看起来近似仿射。
相关代码与矩阵示例
Matrix类核心实现(OpenGL列向量布局)
public final class Matrix4 { public double e11, e12, e13, e14; public double e21, e22, e23, e24; public double e31, e32, e33, e34; public double e41, e42, e43, e44; public void transform(Vec3 v) { double x = v.x, y = v.y, z = v.z; double w = (x * e41) + (y * e42) + (z * e43) + (e44); double norm = 1 / w; v.x = ((x * e11) + (y * e12) + (z * e13) + (e14)) * norm; v.y = ((x * e21) + (y * e22) + (z * e23) + (e24)) * norm; v.z = ((x * e31) + (y * e32) + (z * e33) + (e34)) * norm; } public void perspectiveFovLH(double fovy, double aspect, double znear, double zfar) { double yscale = 1 / tan(fovy * 0.5); double xscale = yscale / aspect; double Q = zfar / (zfar - znear); e11 = xscale; e22 = yscale; e33 = Q; e34 = Q * -znear; e43 = 1; } }
移动相机后的视口-投影-视图矩阵
[ -331.62051997, -616.31014741, -927.45531750, 354532.72229686 158.60434983, -1091.70516425, 427.49500301, 420008.37868979 0.75468455, -0.51045982, -0.41337968, 766.88486375 0.75430721, -0.51020459, -0.41317299, 771.50142132 ]
对应的逆矩阵
[ -0.00058203, -0.00019009, 113.94949731, -112.89669211 -0.00033747, -0.00072765, -84.29481494, 84.34162134 -0.00064586, 0.00055151, -61.14292715, 60.77360931 -0.00000000, -0.00000000, -0.19990000, 0.20000000 ]
核心解析
1. 逆矩阵e41、e42恒为0的原因
视口-投影-视图矩阵是三个矩阵的乘积:M = 视口矩阵 * 投影矩阵 * 视图矩阵,逐个分析各矩阵的结构特性:
- 视图矩阵:本质是相机的逆变换,仅包含旋转和平移,属于仿射矩阵,最后一行固定为
[0, 0, 0, 1]。 - 投影矩阵:你实现的左手系透视矩阵,最后一行是
[0, 0, 1, 0](仅e43为1,其余最后一行元素默认0),仅在z方向引入透视缩放。 - 视口矩阵:负责将NDC坐标转屏幕像素,仅包含缩放和平移,也是仿射矩阵,最后一行固定为
[0, 0, 0, 1]。
当计算这三个矩阵的乘积M时,M的最后一行由投影矩阵的最后一行与视图矩阵相乘后,再与视口矩阵相乘得到。而投影矩阵的最后一行前两个元素为0,结合仿射矩阵的特性,最终M的逆矩阵在计算时,最后一行的前两个元素必然被抵消为0——因为逆矩阵的最后一行需要满足与M的前两列点积为0,而M的前两列来自仿射变换的旋转缩放部分,与投影矩阵的透视方向(z轴)正交。
2. 近似仿射的逆矩阵
你观察到逆矩阵近乎仿射(最后一行前两个元素为0),原因在于:
- 投影矩阵的逆矩阵最后一行前两个元素本身就是0(推导后可知,你的透视矩阵逆矩阵最后一行是
[0, 0, 1/(zfar-znear), zfar/(zfar-znear)])。 - 视图和视口矩阵的逆矩阵都是仿射矩阵(最后一行
[0,0,0,1])。
两者相乘后,逆矩阵的最后一行仅保留投影逆矩阵的z方向相关元素,前两个元素保持为0,因此看起来近似仿射(严格来说不是,因为仿射矩阵最后一行必须是[0,0,0,1],而这里最后一行第三、四个元素非零,用于处理z方向的透视逆变换)。
3. 优化的安全性
你基于e41、e42为0的优化是完全安全的,只要满足以下条件:
- 始终使用仅在z方向引入透视的投影矩阵(比如当前的左手系透视矩阵,最后一行前两个元素为0)。
- 视图矩阵仅包含旋转、平移等仿射变换,不引入透视。
- 视口矩阵仅包含缩放、平移等仿射变换,不引入透视。
只要不修改上述矩阵的类型(比如不要添加x/y方向的透视,或非仿射的视口/视图变换),这个特性就不会失效。
内容的提问来源于stack exchange,提问作者Boann
相关产品推荐
相关产品推荐

