最爱午后红茶

Projection transformations

日期图标
2023-04-18

投影

根据前面的章节我们知道,把三维的物体变成一张二维的照片,需要经过 MVP 的过程,在 View transformations 中我们理解了 View 的过程,在这里,将详细了解 P(即 Projection - 投影)的过程。

投影分为两大类:

  • 透视投影(Perspective projection)
  • 正交投影(Orthographic projection)
透视投影和正交投影(Orthographic projection and Perspective projection)
图一:透视投影和正交投影

透视投影:如图一左图,把摄像机当作一个点,从这个点出发,连出空间中的一个四棱锥(称为视锥体),把某一个平面到另一个平面之间的区域(这个区域也叫做 View Frustum)截出来,并把这个区域内的所有物体转换成一张 2D 的图像,显示在靠近摄像机的那个平面(叫做 Near Clip Plane 或者 Front Clip Plane)上;而远处的平面则叫做 Far Clip Plane(或者 Back Clip Plane)。由棱锥的性质我们知道,近平面是比远平面要小的,假设近平面跟相机的距离一定,且相机固定,则物体拉得跟相机越远,物体在近平面上成像就越小(注意近平面本身的大小不变),有近大远小的特征。这里还有个有趣的梗:)道理我都懂,可是鸽子为什么那么大

正交投影:如图一右图,简单来说可以把正交投影当作是一定条件下摄像机拿到无限远的透视投影,假设透视投影中近平面跟远平面距离保持不变,把相机拉得足够远,近平面和远平面就可以当作是一样大的。也就是在正交投影中,不管把物体拉得多远,它在近平面上成像的大小都是一定的。

正交投影

先通过一个例子来简单理解下在图形学中是怎样给三维物体做正交投影的:

正交投影(Orthographic projection)
图二:正交投影

过程其实很简单,大致分为三步:

  • 把相机转换到标准(原点)位置(也就是前面视图变换说的 Look-at = z-z,Up-direction = yy
  • 把所有点的 zz 坐标去掉(也就是都变成 z=0z = 0,这样它们成像就都落在 xyx-y 平面上了)
  • 把成像结果缩放到 [1,1]2[-1, 1]^2 的矩阵内(主要是为了方便后续的一些计算)

以上只是用作简单理解正交矩阵的成像过程,因为如果仅仅只是这样做的话,对于有交集的物体就无法区分前后了。

而在更正式的描述下,我们会把要成像的物体的最左边(l)、最右边(r)、最上面(t)、最下面(b)、最近处(n)、最远处(f)为边界,把整个物体包在一个长方体内,在把这个立方体的中心平移到原点,进行相应缩放,变成一个 [1,1]3[-1, 1]^3 的立方体,这个立方体叫做标准(正则、规范)立方体(Canonical cube)。

标准立方体(Canonical cube)
图三:标准立方体

这里有一个需要注意的地方就是,教程这里用的是右手系的坐标(即看向 z-z 方向,这样的话,f 的值越小,就表示离相机越远,这对我们来说是有些反直觉的,不过它有它的好处。而如果用的是左手系的坐标,就是符合我们的直觉,但它也有不方便的地方。

用矩阵的方式去表示正交投影也很简单:

  • 平移变换:把包住物体的立方体中心平移到原点(当然其他所有点也要做平移),我们知道立方体的中心是 (r+l2,t+b2,n+f2)(\frac{r + l}{2}, \frac{t + b}{2}, \frac{n + f}{2}),则平移变换的矩阵为:[100r+l2010t+b2001n+f20001]\left[\begin{matrix}1 & 0 & 0 & -\frac{r + l}{2}\\ 0 & 1 & 0 & -\frac{t + b}{2}\\ 0 & 0 & 1 & -\frac{n + f}{2}\\0 & 0 & 0 & 1\end{matrix}\right]
  • 放缩变换:接着把立方体缩放成 2×2×22 \times 2 \times 2 大小(因为 [1,1]3[-1, 1]^3 范围就是 2×2×22 \times 2 \times 2)的立方体。以 xx 轴举例,我们知道缩放前的立方体在 xx 轴边长为 rlr - l,要使其变成 2,则缩放倍数为 2rl\frac{2}{r - l}。类似的,我们知道 yyzz 的缩放倍数分别是 2tb\frac{2}{t - b}2nf\frac{2}{n - f}。则放缩变换的矩阵为:[2rl00002tb00002nf00001]\left[\begin{matrix}\frac{2}{r - l} & 0 & 0 & 0\\ 0 & \frac{2}{t - b} & 0 & 0\\ 0 & 0 & \frac{2}{n - f} & 0\\0 & 0 & 0 & 1\end{matrix}\right]

所以整个正交投影的矩阵形式为:

  • Mortho=[2rl00002tb00002nf00001][100r+l2010t+b2001n+f20001]M_{ortho} = \left[\begin{matrix}\frac{2}{r - l} & 0 & 0 & 0\\ 0 & \frac{2}{t - b} & 0 & 0\\ 0 & 0 & \frac{2}{n - f} & 0\\0 & 0 & 0 & 1\end{matrix}\right]\left[\begin{matrix}1 & 0 & 0 & -\frac{r + l}{2}\\ 0 & 1 & 0 & -\frac{t + b}{2}\\ 0 & 0 & 1 & -\frac{n + f}{2}\\0 & 0 & 0 & 1\end{matrix}\right]

透视投影

透视投影近大远小的特征符合我们直观上视觉效果,而它也是图形学中应用得最广泛的投影。我们知道同一个平面内,两条平行线永不相交,但在透视投影中,两条平行线给我们的视觉效果是,它不仅会相交,而且我们还能知道它的交点在哪:

“相交”的铁轨(Rail)
图四:“相交”的铁轨

由本节开头的描述我们知道,透视投影就是把 Frustum 内的物体投影到近平面上。在正式讲解投影前,我们先看看怎么去定义 Frustum:

透视投影的 Frustum 和正交投影的 Cuboid(Frustum and cuboid)

图五:透视投影的 Frustum 和正交投影的 Cuboid

从前面图三我们知道,在正交投影中可以通过以最左边(l)、最右边(r)、最上面(t)、最下面(b)、最近处(n)、最远处(f)为边界,把整个物体包在一个长方体内。那么在相机的视角里,是怎样定义透视投影中近平面llrrbbtt 的呢?主要是从两个方面去描述:

  • 垂直可视角度(field-of-view or fovY
  • 宽高比(aspect ratio),这里做个假设:l=rl = -rb=tb = -t
相机近平面(camera near plane)
图六:相机近平面

宽高比都好理解,而垂直可视角度在这里的意思是,从相机出发,分别跟近平面上边的中点和下边的中点连一条线形成的夹角。对应的有水平可视角度

一般情况下相机跟近平面的距离 n\vert n\vert(因为相机往 z-z 方向看,n 通常为负数)、垂直可视角度和宽高比是已知的,如何通过它们去求解出近平面的 llrrbbtt 呢?

计算近平面的 lrbt
图七:计算近平面的 lrbt

我们还是可以像分析“挤压”透视投影那样,往 x-x 方向看,只截取上半部分。很容易得出两个等式:

  • tanfovY2=tntan{\frac{fovY}{2}} = \frac{t}{\vert n\vert}
  • aspect=rtaspect = \frac{r}{t}

llrrbbtt 因此也很容易求解了。到这里我们也知道了,要定义一个视锥的话,只需要定义相机跟平面的距离、垂直可视角度和宽高比。

从图五可以看到,在 Frustum 内的物体投影到近平面的过程中,由棱锥的性质我们直观地知道物体会收缩,而收缩程度取决于物体与近平面之间的距离。

透视投影跟正交投影的一个区别是:远平面的大小不一样。这给我们的一个思路是,可以尝试先把远平面“挤压”成跟近平面一样大(Frustum 内的物体也要做相应的缩放),然后再对远平面做正交投影。这个”挤压“的过程中,我们制定一些规则:

  • 近平面保持不变
  • 远平面 zz 坐标不变,即 f 不变
  • 远平面的中心点不变
透视投影 -> 正交投影(Perspective to orthographic)
图八:透视投影 -> 正交投影

我们假设相机跟远平面中心连一条线,这条线要在 zz 轴上,而相机往 z-z 方向看,yy 轴向上。那我们可以截取上半部分的侧视图(往 x-x 方向看)进行分析:

截取相机观测内容的上半部分区域

图九:截取相机观测内容的上半部分区域

上半部分的侧视图
图十:上半部分的侧视图

如图十所示,假设点 P=(x,y,z)P = (x', y', z') 是 Frustum 区域内的点 O=(x,y,z)O = (x, y, z) 被”挤压“之后的位置(如果 z=fz = f 则该点在远平面上)。我们忽略 xx 方向,根据相似三角形我们很容易可以知道:y=nzyy' = \frac{n}{z}y

同理,从上往下看(往 y-y 方向看),我们可以得到 x=nzxx' = \frac{n}{z}x

截取相机观测内容的右半部分区域

图十一:截取相机观测内容的右半部分区域

也就是点 OO 经过一个矩阵变换(也就是”挤压“,假设是 Mpersportho4×4M_{persp \rightarrow ortho}^{4 \times 4})后的齐次坐标形式我们可以写成:

  • Mpersportho4×4(xyz1)=(nx/zny/z?1)M_{persp \rightarrow ortho}^{4 \times 4}\left(\begin{matrix}x\\ y\\ z\\1\end{matrix}\right) = \left(\begin{matrix}nx/z\\ ny/z\\ ?\\1\end{matrix}\right)

而从 2D transformations 中关于齐次坐标下扩充两点相加的说明里面我们知道,对于齐次坐标下的一个点 (x,y,z,w)(x, y, z, w),可以被认为是 (x/w,y/w,z/w,1)(x/w, y/w, z/w, 1),其中 w0w \neq 0。这个定义反过来看也成立,那就有 (nx/zny/z?1)=(nxny?z)\left(\begin{matrix}nx/z\\ ny/z\\ ?\\1\end{matrix}\right) = \left(\begin{matrix}nx\\ ny\\ ?\\z\end{matrix}\right),所以有:

  • Mpersportho4×4(xyz1)=(nxny?z)M_{persp \rightarrow ortho}^{4 \times 4}\left(\begin{matrix}x\\ y\\ z\\1\end{matrix}\right) = \left(\begin{matrix}nx\\ ny\\ ?\\z\end{matrix}\right)

而有了上面这个等式,根据矩阵乘法法则知,Mpersportho4×4M_{persp \rightarrow ortho}^{4 \times 4} 的第一行(假设是 (a,b,c,d)(a, b, c, d))与 (xyz1)\left(\begin{matrix}x\\ y\\ z\\1\end{matrix}\right) 相乘,得到 nx=ax+by+cz+dnx = ax + by + cz + d,很容易得到 n=an = a。如果我们之前不做 (nx/zny/z?1)=(nxny?z)\left(\begin{matrix}nx/z\\ ny/z\\ ?\\1\end{matrix}\right) = \left(\begin{matrix}nx\\ ny\\ ?\\z\end{matrix}\right) 这个转换,那 (a,b,c,d)(a, b, c, d)(xyz1)\left(\begin{matrix}x\\ y\\ z\\1\end{matrix}\right) 相乘会得到:nx/z=ax+by+cz+dnx/z = ax + by + cz + d,即 a=n/za = n/z,这里我理解 zz 作为点 OO 的坐标是个变量,而 aann 是常量,这样没法求解出 aa 的值的。用类似的方法我们可以求解出 Mpersportho4×4M_{persp \rightarrow ortho}^{4 \times 4} 的一部分:

  • Mpersportho4×4=(n0000n00????0010)M_{persp \rightarrow ortho}^{4 \times 4} = \left(\begin{matrix}n & 0 & 0 & 0\\0 & n & 0 & 0 \\ ? & ? & ? & ?\\0 & 0 & 1 & 0\end{matrix}\right)

还剩下第三行需要求解,第三行跟 OO 点的 zz 坐标相关,根据前面关于”挤压“的规则我们能得到两点信息:

  • 任意近平面上的点”挤压“前后坐标完全不变
  • 任意远平面上的点”挤压“前后的 zz 坐标不变xxyy 会受”挤压“操作而改变的)。

这样的话,我们取近平面上的一个点 N=(x,y,n,1)N = (x, y, n, 1),则进行”挤压“之后的坐标仍然是 (x,y,n,1)(x, y, n, 1),我们也可以把它写成 (nx,ny,n2,n)(nx, ny, n^2, n)。这样我们就知道 Mpersportho4×4M_{persp \rightarrow ortho}^{4 \times 4} 的第三行一定需要满足 (0,0,A,B)(0, 0, A, B),由此可得:

  • An+B=n2An + B = n^2

类似的,我们知道,对于远平面的中心点,”挤压“前后也是不变的,我们定义该点为 F=(0,0,f,1)F = (0, 0, f, 1),我们也可以把它写成 (0,0,f2,f)(0, 0, f^2, f)。拿 Mpersportho4×4M_{persp \rightarrow ortho}^{4 \times 4} 的第三行 (0,0,A,B)(0, 0, A, B) 去乘 (0,0,f,1)(0, 0, f, 1) 我们得到:

  • Af+B=f2Af + B = f^2

结合两个式子我们可以求解出 A=n2f2nf=(n+f)(nf)nf=n+fA = \frac{n^2 - f^2}{n - f} = \frac{(n + f)(n - f)}{n - f} = n + fB=nfB = -nf。而 Mpersportho4×4M_{persp \rightarrow ortho}^{4 \times 4} 也就求解出来了:

  • Mpersportho4×4=(n0000n0000n+fnf0010)M_{persp \rightarrow ortho}^{4 \times 4} = \left(\begin{matrix}n & 0 & 0 & 0\\0 & n & 0 & 0 \\ 0 & 0 & n + f & -nf\\0 & 0 & 1 & 0\end{matrix}\right)

整个透视投影 Mpersp=MorthoMpersporthoM_{persp} = M_{ortho}M_{persp \rightarrow ortho} 也就能求解了。

* 未经同意不得转载。