就像茴香豆的“茴”字有四种写法一样,透视投影矩阵细究起来也有好几种写法。

为什么有多种写法?

这个问题来自Games101的第三次作业,细心的朋友会发现,按照闫老师讲课的内容,做出来的结果是上下左右颠倒的一个三角形。

根据网上的解释,会说因为Games的证明过程是左手系,而作业使用的OpenGL是右手系,因此要给透视矩阵乘上一个负号。这个解释提到了左右手系的区别,但是解释还不够完整。

其实透视矩阵是为了满足透视效果而创造的变换矩阵,由于它只是为了实现透视效果,因此它本身就不是唯一的。

回到推导过程

Games101给出了详细且完整的推导过程,我们知道,一个完整的透视投影矩阵可以看作是正交投影矩阵和挤压变换的乘积。

$$ M_{persp} = M_{ortho}\times M_{persp\to ortho} $$

这个挤压变换,我们可以利用三角形相似的知识,推导出它的矩阵式。

$$ Mat_{persp\to ortho}=\left(\begin{array}{cccc} n & 0 & 0 & 0 \\ 0 & n & 0 & 0 \\ 0 & 0 & n+f & -n f \\ 0 & 0 & 1 & 0 \end{array}\right) $$

由于矩阵的正交投影可以写成

$$ M_{ortho}=\left(\begin{array}{cccc} \frac{2}{r-l} & 0 & 0 & -\frac{l+r}{r-l} \\ 0 & \frac{2}{t-b} & 0 & -\frac{b+t}{t-b} \\ 0 & 0 & \frac{2}{f-n} & -\frac{n+f}{f-n} \\ 0 & 0 & 0 & 1 \end{array}\right) $$

当相机位于Z轴上时,即视锥体位于画面中心,有$l+r=0, b+t=0$。因此我们可以得到最终的投影矩阵

$$ M_{persp}=\left(\begin{array}{cccc} \frac{2 n}{w} & 0 & 0 & 0 \\ 0 & \frac{2 n}{h} & 0 & 0 \\ 0 & 0 & \frac{n+f}{f-n} & -\frac{2 n f}{f-n} \\ 0 & 0 & 1 & 0 \end{array}\right) $$

以OpenGL为代表的右手系

所谓右手系,就是满足右手定则的坐标系,相机设置为面向Z轴的负方向。这会造成f和n都是负值,即n=-zNear,f=-zFar(其中zNear和zFar是绝对距离)。

我们把它们代入到上面推导的矩阵中,得到

$$ M_{persp}=\left(\begin{array}{cccc} -\frac{2 zNear}{w} & 0 & 0 & 0 \\ 0 & -\frac{2 zFar}{h} & 0 & 0 \\ 0 & 0 & \frac{zNear+zFar}{zFar-zNear} & \frac{2 zNear zFar}{zFar-zNear} \\ 0 & 0 & 1 & 0 \end{array}\right) $$

由于一般不希望X和Y的缩放系数是负数,所以我们可以把符号提到齐次分量当中

$$ M_{persp}=\left(\begin{array}{cccc} \frac{2 zNear}{w} & 0 & 0 & 0 \\ 0 & \frac{2 zFar}{h} & 0 & 0 \\ 0 & 0 & -\frac{zNear+zFar}{zFar-zNear} & -\frac{2 zNear zFar}{zFar-zNear} \\ 0 & 0 & -1 & 0 \end{array}\right) $$

这个矩阵就是OpenGL采用的透视矩阵,也是Mathematics for 3D Game Programming and Computer Graphics一书中的直接推导结果。

我们比对这个透视矩阵和上一小节末尾的透视矩阵,这也就解答了作业三为什么要奇怪地把透视矩阵要将Z分量取负号,而不是将整个透视矩阵都乘上一个负号。归根结底是因为OpenGL传入的是绝对距离zNear和zFar。

我们可以直接将zFar和zNear代入OpenGL方法,生成透视矩阵验证这个推论。

glm::mat4 MatPerspTest1 = glm::perspective(glm::radians(45.0f), 1920.0f/1080.0f, 0.1f, 50.0f);

从另外一个角度理解,透视矩阵的目的是将视锥体中的内容挤压到$[-1, 1]^3$空间内。其中Z方向上映射到[-1, 1]。由于n>f(两个都是负值,f越远反而越小),想要的映射的结果却又是$n\to-1, f\to1$。因此,出现了Z轴的翻转,所以才会仅将Z分量取负号。

以DirectX为代表的左手系

左手系,相机朝向的是Z轴的正方向。那么,带入绝对距离的透视矩阵就和我们在Games101推导的没有差别。

$$ M_{persp}=\left(\begin{array}{cccc} \frac{2 zNear}{w} & 0 & 0 & 0 \\ 0 & \frac{2 zFar}{h} & 0 & 0 \\ 0 & 0 & \frac{zNear+zFar}{zFar-zNear} & -\frac{2 zNear zFar}{zFar-zNear} \\ 0 & 0 & 1 & 0 \end{array}\right) $$

小结

所以综合以上的角度来看,闫老师的推导式是更通用的做法,既可以用于左手系,也可以用于右手系。只是说,需要分别带入正负值,考虑它们对透视矩阵形式的影响。

除了手性,压缩尺度也会产生不同的透视投影矩阵

这里我们以UE的投影矩阵为例

$$ M_{persp}=\left(\begin{array}{cccc} \frac{2 zNear}{w} & 0 & 0 & 0 \\ 0 & \frac{2 zFar}{h} & 0 & 0 \\ 0 & 0 & \frac{zFar}{zFar-zNear} & -\frac{zNear zFar}{zFar-zNear} \\ 0 & 0 & 1 & 0 \end{array}\right) $$

为什么会是这个样子呢?

这是因为,UE的透视投影过程中,将视锥体压缩到了$[-1,1]^2\times[0,1]$的立方体空间中。即在Z方向上的映射是$n\to0, f\to1$,这导致了$M_{ortho}$的变化

$$ M_{ortho}=\left(\begin{array}{cccc} \frac{2}{w} & 0 & 0 & 0 \\ 0 & \frac{2}{h} & 0 & 0 \\ 0 & 0 & \frac{1}{f-n} & -\frac{n}{f-n} \\ 0 & 0 & 0 & 1 \end{array}\right) $$

由$M_{persp}=M_{ortho}\times M_{persp\to ortho}$不难推导出以上的透视矩阵。

更加实用的定性推导

以上这些透视投影矩阵变化这么多,要牢记并推导不是一件容易的事。这里我从OneLoneCoder的教程中学到透视投影的一种定性推导方式,这是一种工程师思维,可以帮助理解。

Y轴

首先,FoV和近平面之间的关系是固定的

$$ \tan\frac{FoVY}{2} = \frac{h}{2n} $$

从打游戏感知的结果来看,FoV会影响透视的效果。具体来说,FoV越大,原先视锥体的物体就会显得更小。因此y值和FoV呈现一个反比的关系。

$$ y \to \frac{y}{\tan\frac{FoVY}{2}} $$

X轴

x轴同理。并且由于宽高比是固定的,我们可以直接从FoVY计算出FoVX。

$$ x \to \frac{x}{\tan\frac{FoVX}{2}} = \frac{x}{aspect\_ratio\times \tan\frac{FoVY}{2}} $$

Z轴

z轴较为复杂一点。我们想象一个视锥体的挤压,它将$[0, f]$的锥体空间挤压到$[n, f]$之间的截头锥体,并且保持比例不变。

$$ z\to\frac{zf}{f-n} $$

保持挤压的系数不变,我们考虑到offset,所以还存在一个位移

$$ z\to\frac{f(z-n)}{f-n} $$

W轴

因为透视投影受Z的大小影响,Z越大,相应的物体越小,即X和Y会越小。因此,最后的结果应该是所有项都除以原来的Z值,为了方便把它放到齐次分量中。

$$ w = z $$

基于定性推导的投影矩阵

综上,我们得到基于定性分析的变换

$$ [x, y, z] \to [\frac{x}{az\tan\frac{FoVY}{2}}, \frac{y}{z\tan\frac{FoVY}{2}}, \frac{f}{f-n}-\frac{nf}{z(f-n)}] $$

写成投影矩阵的形式就是

$$ M_{persp}=\left(\begin{array}{cccc} \frac{1}{a\tan\frac{FoVY}{2}} & 0 & 0 & 0 \\ 0 & \frac{1}{\tan\frac{FoVY}{2}} & 0 & 0 \\ 0 & 0 & \frac{zFar}{zFar-zNear} & -\frac{zNear zFar}{zFar-zNear} \\ 0 & 0 & 1 & 0 \end{array}\right) $$

居然就是我们之前推导出的UE投影矩阵,不愧是工程经验和理论推导的结合,这真是太奇妙了~

Last modification:March 12, 2024
如果觉得我的文章对你有用,请随意赞赏