本文最后更新于 2025年12月23日 晚上
【技术美术】个人引擎内的投影矩阵
Unity 中官推的投影矩阵风格是 OpenGL,这可以直接从相机组件中拿到,但在DX平台,Unity传入着色器时实际上会将该矩阵转为 DX 风格。
DX 风格没必要了解,而且此处也不是 Unity 中使用的 DX 风格,而是个人自制游戏引擎时做的推导文章。
此处采用的NDC空间为:
- x:从左到右 (-1,1)
- y:从下到上 (-1,1)
- z:从前到后 (0,1)
正交投影
投影过程就是简单的范围缩放偏移,没有近大远小的效果,难以看出深度关系,常用于 2D 游戏。
若视锥体参数如下:
- l:视锥体左平面
- r:视锥体右平面
- b:视锥体下平面
- t:视锥体上平面
- n:视锥体近平面
- f:视锥体远平面
则投影矩阵为:
ortho(r,l,t,b,n,f)=r−l20000t−b20000f−n10−r−lr+l−t−bt+b−f−nn1
特别的,当视锥体的左右、上下平面坐标对称时:
ortho(r,t,n,f)=r10000t10000f−n1000−f−nn1
推导过程
https://zhuanlan.zhihu.com/p/474879818
正交投影中从相机空间转换到裁剪空间,是一个方形空间到另一个方形空间的转换,所以只需分别考虑三个轴的各自变化就行,若根据直线实现映射,可构成以下公式:
XnYnZn=k1Xe+b1=k2Ye+b2=k3Ze+b3
其中 n 下标表示转换后的剪辑空间坐标,e 表示原本的视图空间坐标。
当有以下参数:
- l:视锥体左平面
- r:视锥体右平面
- b:视锥体下平面
- t:视锥体上平面
- n:视锥体近平面
- f:视锥体远平面
映射效果如下(要注意图片使用的是 OpenGL 剪辑空间, Direct3D 的 Z 的计算与其不同):

带入参数计算后即可推出:
k1b1=r−l1−(−1)=Xn−k1Xe=1−r−l1−(−1)∗r=r−lr−l−r−l2r=−r−lr+l
k2b2=t−b1−(−1)=Yn−k2Ye=1−t−b1−(−1)∗t=t−bt−b−t−b2t=−t−bt+b
k3b3=f−n1−0=Zn−k3Ze=1−f−n1−0∗f=f−nf−n−f−nf=−f−nn
再将这种映射方法套用到矩阵即可得到
ortho=r−l1−(−1)0000t−b1−(−1)0000f−n1−00−r−lr+l−t−bt+b−f−nn1
透视投影
与人眼一样,随深度不同,具有近大远小的效果,容易看出物体距离远近,常用于 3D 游戏。
若视锥体参数如下:
- n:近平面
- f:远平面
- v:视野上下角度
- r:视野宽高比
Perspective=Ortho(tan(2v)n/r,tan(2v)n,n,f)∗n0000n0000n+f100−nf0
推导过程
-
问题分析
透视投影的空间是一个四棱台,将其变换到剪辑空间的步骤可以拆成两步:
- 将四棱台缩放为长方体,实现近大远小。
- 将长方体缩放到剪辑空间,等同于正交投影。
由于正交投影已推导,故现在只需要求出将四棱台缩放为长方体的方法就行。
-
公式推导
https://www.zhyingkun.com/perspective/perspective/

由于缩放为长方体,故对于处在视锥线上的点,缩放后应与在近平面的 x,y 坐标一致。以上图为例,应有:
f(y)=ys
根据相似三角形原理,可得:
dysys=zy=zyd
由于 d 就是近平面距离,若近平面距离为 n,则:
f(y)f(x)=zyn(前面说过ys=f(y))=zxn(x、y缩放一致)
-
矩阵推导
现在要将该公式反应在矩阵变换上:
- 对于 n,这是一个定值,直接利用缩放矩阵的原理就可以实现。
- 对于 z,这是一个变量,肯定无法直接写在矩阵中,但可以借助其次坐标 w 归一化的特性,将向量的 w (位置在 m43)设为 z 即可。
于是便可得出初步矩阵:
n0?00n?000?100?0
其中 z 的系数都被标记为?,因为 z 也会受 w 归一的影响,而我们实际需要 z 保持不变,故需要对这些能对 z 产生作用的系数进行推导,以确保最终计算出的向量归一化前的 z 分量为z2。
由于前两个系数(m31,m32)是与 x,y 相乘,我们不需要所以始终为 0,又因为输入向量的 w 分量默认为 1,此时当剩余的两个系数(m33,m34)分别为 A,B 时,可将问题写成以下公式:
Az+B=z2
带入 n(近平面 z),f (远平面 z)两个已知实例:
An+BAf+B=n2=f2
利用消元法可得出:
A(n−f)AA=n2−f2=n−f(n+f)(n−f)=n+f
(n+f)n+BBB=n2=n2−(n2+fn)=−nf
于是最终矩阵可得出为:
n0000n0000n+f100−nf0
线性深度推导
利用透视投影计算出的新深度是非线性的。因为利用 (n+f)z−nf 抵消除 z 得出的新 z,只能保证远近平面一致,而其他值域则会受到非线性变化(具体变化如下图)。因此当希望得到远近平面间的线性深度时,必须要进行进一步处理。

投影函数
首先归纳出各种投影对 z 的影响函数:
-
正交
z′=f−nz−n
-
逆正交
z=z′(f−n)+n
-
透视
z′=z(n+f)z−nf
-
逆透视
zzz−z′(n+f)zz(1−z′n+f)zz=z′(n+f)z−nf=z′(n+f)z−z′nf=−z′nf=−z′nf=−z′−(n+f)nf=n+f−z′nf
投影过程
正常的投影过程如下:
z[n,f]非线性=透视(z[n,f]线性)z[0,1]非线性=正交(z[n,f]非线性)
由此可以逆推出 z[0,1]线性 的求解过程:
z[n,f]非线性=逆正交(z[0,1]非线性)z[n,f]线性=逆透视(z[n,f]非线性)z[0,1]线性=正交(z[n,f]线性)
即
z[0,1]线性zzzzzz=正交(逆透视(逆正交(z[0,1]非线性)))=f−n(n+f−(z′(f−n)+n)nf)−n=f−n(f−z′(f−n))nf)−n=f−nn(f−z′(f−n))f−1)=f−nn(f−z′(f−n))f−(f−z′(f−n)))=f−nn(f−z′(f−n))z′(f−n))=f−z′(f−n))nz′