裁切空间变换
这篇文章将主要讨论计算机图形学中的裁切空间和射影空间。
仿射空间
作为序章,我们首先介绍一下仿射空间(Affine space)。 仿射空间相对于普通的欧几里得空间增添了“位置”的概念,一般的欧氏空间中向量都是“自由向量”,即只具有大小和方向,不考虑起点和终点。 而在仿射空间中,我们考虑的都是具有位置的“点”,将两个点相减,才能得到连接两个点的向量。
仿射空间的定义
我们首先考虑仿射空间的定义。
考虑数域$K$上的向量空间$V$,对非空集合$E$,若其装备有映射 \(\begin{array}{cccc} \phi: & E^2 & \to & V \\ & (A, B) & \mapsto & \vec{AB} \end{array}\) 且该映射满足: \(\forall A, B, C \in E, \; \vec{AB} + \vec{BC} = \vec{AC},\) 且 \(\forall A \in E, \, \forall \vec v \in V, \, \exists! B \in E \quad \text{s.t.} \; \vec v = \vec{AB},\) 那么称$E$仿射空间(espace affine),其中的元素称为点。 $V$称为该仿射空间的方向空间(direction de l’espace affine),也记为$\vec E$。
也有借助群作用(Group action;Action de groupe)进行的定义,但这种更初等的定义比较适合当前的研究。
向量空间$V$中的向量也称为平移(translation)。 第二条性质也叫平移的存在且唯一性。 这意味着将仿射空间中的任何点$A$沿任何方向$\vec u$平移,都能得到唯一的另一个点$B$。
只要在仿射空间中任意选择一个点$O$,那么映射 \(\begin{array}{cccc} \phi_O: & E & \to & V \\ & A & \mapsto & \vec{\mathrm{OA}} \end{array},\) 就是从$E$到$V$的双射,这意味着本质上讲仿射空间和线性空间是同构的。 因此任何仿射空间中的研究都可以通过选择原点被还原到线性空间中。
同时,在另一方向上,任何线性空间自己也是一个仿射空间: \(\begin{array}{cccc} \varphi: & V \times V & \to & V \\ & (\vec u, \vec v) & \mapsto & \vec v - \vec u \end{array}.\) 这个性质有时叫做向量空间的典范仿射结构(la structure affine canonique)。 这也是向量$\vec{AB}$能被写为$B - A$的原因:前者是向量空间中的向量,后者是典范仿射空间中的点。
综上所述,在同构这一等价关系下,任何仿射空间和其方向空间都属于同一个等价类。
仿射空间坐标
我们接着考虑仿射空间中的计算,这需要使用仿射空间的坐标表示。
考虑数域$K$上的向量空间$V$和$V$上的仿射空间$E$,其上的坐标系(Repère cartésien)$\mathcal R$定义为$E$中任意一点$O$和$V$上的基底 \(b = (b_1, \dots, b_n),\) 构成的有序对 \(\mathcal R = (O; b).\) 点$O$称为该坐标系的原点(Origine du repère)。
任何$E$中的点$M$在该坐标系下的坐标,是原点$O$到该点的向量在基底$b$下的坐标,即 \(M_{\mathcal R} = \vec{OM}_b \in K^n.\) 这个坐标的定义是基于上面介绍过的事实,即一旦选定原点$O$,映射$\phi_O$是双射。 由于向量在任意基底下的坐标表示是唯一的,这意味着任何点的坐标表示也是唯一的。
接下来考虑仿射空间中的坐标变换:
考虑同一仿射空间上的两个坐标系 \(\mathcal R = (O; b), \; \mathcal R' = (O'; b'),\) 若仿射空间中有一点$M$,其在两个坐标系下的坐标写为: \(M_{\mathcal R } = \begin{pmatrix} x_1 \\ \vdots \\ x_n \end{pmatrix}, M_{\mathcal R'} = \begin{pmatrix} x_1' \\ \vdots \\ x_n' \end{pmatrix},\) 那么有 \(\left\{ \begin{aligned} x_1' &= a_{11} x_1 + \cdots + a_{1n} x_n + b_1 \\ & \vdots \\ x_n' &= a_{n1} x_1 + \cdots + a_{nn} x_n + b_n \\ \end{aligned} \right..\) 其中$(a_{ij})$是从$b’$到$b$的基变换矩阵,$b_i$是$\mathcal R$的原点$O$在$\mathcal R’$中的坐标。
这个仿射方程组可写为 \(M_{\mathcal R'} = P_{b' \to b} M_{\mathcal R} + O_{\mathcal R'}.\) 我们可以将$M$写为增广坐标,此时这个方程组变为 \(\begin{pmatrix} M_{\mathcal R'} \\ 1 \end{pmatrix} = \begin{pmatrix} P_{b' \to b} & O_{\mathcal R'} \\ \mathbf 0 & 1 \end{pmatrix} \begin{pmatrix} M_{\mathcal R} \\ 1 \end{pmatrix},\) 这是进行数值操作时仿射空间坐标的最常见形式。
如同在线性空间中,我们将所有线性变换视为坐标系的变换一样,在仿射空间中,任何仿射变换实际上也是坐标系的变换。 因此,上文所述的增广矩阵也叫做仿射变换矩阵。
射影空间
在计算机图形学中,我们基本总是在仿射空间(Affine space)中进行操作。 之前我们介绍过,利用增广矩阵,$n$维仿射空间中的仿射变换,可由$n+1$维“欧氏空间”中的线性变换来表示,仅需要将多出的一维坐标置为一即可。 多出的一维坐标还有助于我们区分向量和点,因为两个点相减后多出的那一维坐标总是零。
这些约定看上去毫无破绽,直到我们遭遇“射影变换”。
从仿射空间到射影空间
仿射变换总是保证平行关系不变:两条平行的直线,在仿射变换之后仍然是平行线。 射影变换则与其不同,正如我们美术课上学到的那样,两条平行的直线,在经过相机的射影变换之后,会相交于无穷远处。 再考虑图形学中的裁切空间,似乎在仿射空间中应用射影变换,再进行透视除法,就能回到线性空间,显然射影空间与仿射空间是两个密切相关的概念。 那么,射影空间具有什么特殊的性质呢?
首先我们考虑简单的低维情况,即二维射影实空间$\mathbb{RP}^2$,这正是绘画或摄影所在空间。 以上图为例,可以发现,二维射影空间中(即画中或相片中)相同的两个点,在三维空间$\mathbb R^3$中竟连成了一条直线。 这揭示了射影空间的代数定义:在代数中,如果我们希望把一类元素重新定义成一个元素,就会借助等价关系、等价类和商这种代数工具。 注意到两向量共线的等价关系定义为 \(x \sim y \iff \exists \lambda \neq 0, \; x = \lambda y,\) 我们给出以下定义:
考虑$\mathbb R^{n+1}$中的等价关系 \(x \sim y \iff \exists \lambda \neq 0, \; x = \lambda y,\) 该等价关系在集合$\mathbb R^{n+1} \backslash \{0\}$中的等价类构成的空间称为$n$维实射影空间(Real projective space),记为$\mathbb {RP}^n$。 射影空间中的元素称为点(Point)
注意在定义中我们删去了原点$(0, \dots, 0)$。 如图中所示,$\mathbb R^{n+1}$的原点可产生无穷条直线,因此对应射影空间中的无穷个点。 为避免发生这种情况,我们直接将其从定义中删去。 射影空间中的“原点”位于直线$(0, \dots, 0, \lambda)$上。
仿射空间和射影空间之间的关系由中心投影给出。 仍以$\mathbb{RP}^2$为例,在$\mathbb R^3$仿射空间中任意选择一点,称为投影中心$O$,再选择任意不经过投影中心的平面,称为投影平面$P \simeq \mathbb R^2$,则中心投影将投影平面中的任何一点$X \in P$映射到直线$OX$,从而构造出射影空间$\mathbb{RP}^2$。
我们知道,仿射空间中不具有“原点”的概念,而只要固定了原点,那么仿射空间就完全等同于向量空间。 因此这一操作可完全在向量空间中进行,投影中心自然选择在原点$(0,0,0)$,而投影平面一般选择为$z = 1$。
根据这种构造,射影空间$\mathbb {RP}^n$被定义为$\mathbb R^{n+1}$中所有过原点的直线(即上文提到的$OX$)的集合,这是和上文的代数定义等价的几何定义。
最后,注意到投影平面$P \simeq \mathbb R^2$同构于二维欧氏空间,从而也可以构造一个仿射空间,这意味着其中的任何线性变换和仿射变换实际上都是$\mathbb{RP}^2$上的射影变换。 另一方面,由于$\mathbb{RP}^2$又是由$\mathbb R^3$中的直线构造出来的,因此$\mathbb R^3$上的任何线性自同构,由于具有线性从而保持共线关系,都能诱导一个$\mathbb{RP}^2$上的变换,这个变换正是一个射影变换;反之,$\mathbb{RP}^2$上的射影变换也可构造$\mathbb R^3$中的线性变换。 综上所述,射影空间$\mathbb{RP}^n$上的射影变换,既可表示$\mathbb{R}^n$上的仿射变换,又可由$\mathbb{R}^{n+1}$上的线性变换表示。 这就是图形学和计算机视觉中总是可以使用比目标空间的维数高一维的矩阵表示任何线性变换的原因。
射影空间$\mathbb{RP}^n$和$\mathbb{R}^{n}$与$\mathbb{R}^{n+1}$两个向量空间都有很密切的关系,可能导致混淆。 一个简单的理解方式是将$\mathbb{R}^n$空间视为投影后的空间,即“画布”或“相片”;而将$\mathbb{R}^{n+1}$空间视为投影前的空间,即物体实际存在的空间。
齐次坐标
有了空间之后,我们要寻找的自然是空间的坐标表示,因为有了坐标表示才能进行各种计算。 注意到射影空间直接构建在高一维的欧氏空间上,我们可以直接使用这个欧氏空间的坐标表示。 但是要注意,和欧氏空间不同,射影空间中的坐标,只有坐标之间的“比值”才有意义。
对实射影空间$\mathbb {RP}^n$中的点$p$,其对应的等价类$\{\lambda x\} \subset \mathbb R^{n+1}$中任何一个元素在$\mathbb R^{n+1}$中的坐标,称作该点的齐次坐标(Homogeneous coordinate),记为 \(p = (\lambda x_1, \dots, \lambda x_n, \lambda) = [\lambda x_1 : \dots : \lambda x_n : \lambda],\) 这意味着任何一点均具有无穷多个齐次坐标,习惯上主要取$\lambda = 1$的坐标进行计算。
欲将任何$\mathbb R^{n+1}$中的向量转化到射影空间中,可利用中心投影进行变换,通常这意味着取$\lambda = 1$。 这相当于将向量利用典范映射变换到其等价类中。 反之,从射影空间的齐次坐标回到射影平面,即$\mathbb R^n$空间中,则需要进行中心投影的逆变换,将所有分量除以最后一个分量。 这个操作在图形学中称为透视除法(Perspective division)。
这个坐标被称为齐次坐标,是因为仅当某个函数是齐次的,才能在齐次坐标下表示曲线或曲面。 如果函数不是齐次的,那么 \(f(x) = 0 = f(\lambda x),\) 不一定成立,因此本应表示同一点的坐标,有些会在曲面上,而有些不在曲面上。 若函数是齐次的,即存在$k$,满足 \(f(\lambda x) = \lambda^k f(x),\) 那么有 \(f(\lambda x) = \lambda^k f(x) = 0 = f(x),\) 因此在原空间中在同一曲面上的点,在齐次坐标下依然在同一曲面上。
利用齐次坐标可以进行一些简单的证明。
我们之前讲过,对于射影空间$\mathbb {PR^n}$,任何欧氏空间$\mathbb R^{n+1}$中的可逆线性变换,由于保持线性,都是这个射影空间中的射影变换。 反过来说,射影变换也都是欧氏空间中的线性变换。
考虑由欧氏空间$\mathbb R^{n+1}$上的射影空间$\mathbb{PR^n}$。 设$F$为$\mathbb R^{n+1}$上的自同构,那么这个自同构会诱导射影空间$\mathbb{PR^n}$上一个保留线性的双射,这样的映射称为射影变换(Projective transformation)或单应(Homography)。
关于射影变换实际上有两个不同的定义:
- 射影变换是射影空间$\mathbb{PR^n}$上的同构;
- 射影变换是欧氏空间$\mathbb R^{n+1}$上的同构在射影空间上诱导的映射。
这两个定义是等价的。
考虑自同构$F$,这个映射诱导的射影变换记为$f$。 由于$F$是双射,因此其将空间中的所有直线(一维子空间)$\lambda x$可逆地映射到另一条直线$\lambda F(x)$上。 这就是说对射影空间中的任何非零点$p = [\lambda x]$,映射 \(f(p) = f([\lambda x]) = \{F(\lambda x)\} = \{\lambda F(x)\}\) 也是双射。 因此我们证明了线性空间的自同构一定能产生一个射影变换。 另一方面,即射影变换一定对应线性空间的自同构,则需要借助实数上的射影几何基本定理完成。 这个定理说明:对维数大于二的实射影空间,任何将直线映射到直线的映射(Collineation)都能由线性空间上的自同构诱导产生。 这个定理超出了本文的内容,因此不在此处进行证明。
这个命题说明任何$\mathbb{PR^n}$上的单应都能由线性空间$\mathbb R^{n+1}$上的矩阵表示出来。 这个矩阵叫做单应矩阵。 即使同一坐标系下的同一单应变换,也具有多个单应矩阵,这是因为根据齐次坐标的性质,在其上乘任何非零常数,表示的还是同一个单应变换。 在不考虑无穷远点(下文会介绍)的情况下,单应变换相当于直接在齐次坐标上做矩阵乘法。 这一点在数值计算上得到了广泛的应用。
线性空间$\mathbb R^{n+1}$中的不平行于投影平面的平行直线,投影到射影空间$\mathbb {RP}^n$中后交于一点。
不失一般性地,设投影平面位于$x_{n+1} = 1$。 考虑线性空间中的直线 \(l: \vec p + \lambda \vec v;\; \vec p, \vec v \in \mathbb R^{n+1}, \lambda \in \mathbb R,\) 其不平行于投影平面,因此 \(v_{n+1} \neq 0.\) 直线上一点的坐标为 \(\begin{aligned} x(\lambda) &= [p_1 + \lambda v_1 : \cdots : p_n + \lambda v_n : p_{n+1} + \lambda v_{n+1}] \\ &= [\frac{p_1 + \lambda v_1}{p_{n+1} + \lambda v_{n+1}} : \cdots : \frac{p_n + \lambda v_n}{p_{n+1} + \lambda v_{n+1}} : 1 ]. \end{aligned}\) 从而无穷远点的齐次坐标为 \(\lim_{\lambda \to \infty} x(\lambda) = [\frac{v_1}{v_{n+1}} : \cdots : \frac{v_n}{v_{n+1}} : 1],\) 由于齐次坐标的性质,坐标上乘任何常数依然表示同一个点,因此该点实际上与投影平面的选择无关,只取决于$\vec v$的方向。 该点在投影平面上的坐标为 \(x_{\infty} = (\frac{v_1}{v_{n+1}}, \cdots, \frac{v_n}{v_{n+1}}).\) 这个坐标只与直线的方向有关,因此所有平行线均交于此点,投影平面上的这个点称为灭点(Vanishing point)。
齐次坐标还具有很多有趣的性质,其中重要的一项是它揭示了射影空间中点和线的一致性。 仍以$\mathbb{RP}^2$为例,我们知道其中的点$[x:y:z]$均在$\mathbb R^3$的直线上,即满足 \(a x + b y + c z = 0.\) 如果我们交换常量和变量的位置,即将$x,y,z$定为常量,而将$a,b,c$定为变量,那么立刻可以注意到$[a:b:c]$也是一组有效的齐次坐标。 在射影空间之中,点和线本质上没有区别: 因为射影空间中的直线必须经过原点,因此一个点即可确定一条直线。 这种性质称为射影几何的对偶性(Duality)。 如果没有对偶性,那么在射影几何中必须分别考虑有方向无位置的直线和有位置无方向的点,这会使得研究更加复杂。
无穷远点
齐次坐标的另一优点在于其可表示“无穷远处”的点。 继续以$\mathbb {RP}^2$为例,当我们将欧氏空间$\mathbb R^2$中的任何一点变换到射影空间中时,有一类特殊的齐次坐标不会出现:$[x : y : 0]$。 在进行中心投影时,这些点和位于投影中心上和投影平面平行的平面上,因此没有同时经过投影中心、投影平面和这些点的直线。 因此,这些点不能代表欧氏空间中“有穷远”处的点。
现在考虑$\mathbb R^2$空间。 若我们认为其中的两条直线在“无穷远”处相交,那么这个交点应当满足什么性质? 考虑其中的一系列直线,定义为 \(ax + by + c = 0,\) 其中$a,b$为相同的常数。 出于简单考虑,我们只研究经过原点的曲线,即 \(ax + by = 0.\) 写成参数曲线的形式,有 \(x = bt,\; y = -at,\) 从而任何其上的点的齐次坐标可写为 \(p = [b : -a : \frac{1}{t}].\) 在无穷远处有$\vert 1/t \vert \to 0$,即 \(p = [b : -a : 0].\) 我们知道任何射影空间中的平行直线在无穷远处应当相交于定点,因此该点就是“无穷远”处的交点。 这里的无穷远点指是相对于$\mathbb R^2$空间,即投影之后的像,而言的。 前面我们已经证明过,$\mathbb R^3$空间中不平行于投影平面的平行直线,在投影后也交于一点,但是该点是在投影平面上的点,而非这里所说的无穷远点。 但是,通过增加无穷远点,现在即使平行于投影平面的平行直线,也能交于一点。
线性空间$\mathbb R^{n+1}$中的任何平行直线,投影到射影空间$\mathbb {RP}^n$中后交于一点。
对不平行于投影平面的平行直线的情况已经证明过了。 对平行于投影平面的平行直线的情况,证明如下。 仍设投影平面为$x_{n+1} = 1$, 设直线 \(l: \vec p + \lambda \vec v;\; \vec p, \vec v \in \mathbb R^{n+1}, \lambda \in \mathbb R,\) 其平行于投影平面,因此 \(v_{n+1} = 0.\) 直线上一点的坐标为 \(\begin{aligned} x(\lambda) &= [p_1 + \lambda v_1 : \cdots : p_n + \lambda v_n : p_{n+1}] \\ &= [\frac{p_1 + \lambda v_1}{p_{n} + \lambda v_{n}} : \cdots : 1 : \frac{p_{n+1}}{p_n + \lambda v_n}]. \end{aligned}\) 取极限,得到 \(x_\infty = [\frac{v_1}{v_n} : \cdots : 1 : 0],\) 即相交于无穷远点。 该坐标只取决于$\vec v$的方向,因此所有平行直线相交于同一点。
根据以上说明,齐次坐标$[x : y : 0]$代表的是射影空间中“无穷远处”的点。 根据射影几何的对偶性,齐次坐标$[0 : 0 : z]$代表了一条直线,这条直线称为无穷远直线。
使用$z=0$的点代表无穷远点还有一些额外的优势: 显然,无穷远处的点不受平移的影响,因此对仿射变换仅有线性部分能起作用。 这种点可以表示线性空间中的表示方向的自由向量而非仿射空间中有位置的点。
加入无穷远点和无穷远直线之后,$\mathbb{RP}^n$所处的$\mathbb{R}^{n+1}$线性空间的性质发生了更多的变化。 上图展示了$\mathbb{RP}^2$空间的例子。 我们可将射影空间中的每一个点看作对应线性空间中的一条直线。 若这条直线不在平面$z = 0$内,如同图中蓝色直线一样,那么它表示的就是有穷远处的点,这些点在每一个“画布”(即投影平面)上唯一确定一个点; 而若这条直线在平面$z = 0$内,如同图中橄榄色直线一样,那么它表示的就是无穷远处的点,这些点在投影平面上没有对应的点,但可视为任何投影平面上的直线在无穷远处的极限。
仔细观察可以发现,射影空间中的平行线必然交于一点。 我们又知道,圆上的平行线必然交于两个对跖点。 如果将两个对跖点视为同一点,那么射影空间就和圆非常相似。 实际上,实射影空间同胚于对跖点视为同一点的球面,这个球面叫做射影球面。
图形学中的应用
最后让我们结束这些抽象的讨论,回到图形学的应用上。 完整的渲染管线中,具有重要意义的空间和变换总结如下。
首先是一般在顶点着色器中完成的步骤: \(\begin{array}{c} \text{模型空间} \\ \small \mathbb R^3 \end{array} \xrightarrow{\text{模型矩阵}} \begin{array}{c} \text{世界空间} \\ \small \mathbb R^3 \end{array} \xrightarrow{\text{视图矩阵}} \begin{array}{c} \text{相机空间} \\ \small \mathbb R^3 \end{array} \xrightarrow{\text{投影矩阵}} \begin{array}{c} \text{裁切空间} \\ \small \mathbb{RP}^3 \end{array}\) 这里注意到模型矩阵和视图矩阵都是仿射空间中的变换,因此其一定满足: \(M, V = \begin{pmatrix} R & \vec T \\ \mathbf 0 & 1 \end{pmatrix},\) 其中$R$是旋转和缩放部分,$T$是平移部分。 为节省带宽,有些时候会把最后一行省略掉。 投影矩阵则比较特别。 对于透视相机,投影矩阵进行的正是上文所述的中心透视,这种特殊的射影变换称为透视变换(Perspectivity);但对于正交相机,则不是透视变换、而是$\mathbb R^4$中的仿射变换,因此依然是射影变换。 在利用投影矩阵进行变换之前,需要先将点从$\mathbb R^3$仿射空间变换到$\mathbb{RP}^3$射影空间,这是通过将新坐标设为$w = 1$来完成的。 为方便与增广矩阵进行运算,仿射空间中的坐标通常已经具有这样的形式了。
接下来裁切空间中进行裁切并变换到正则化设备坐标(Normalized Device Coordinate)和屏幕坐标上,一般由固定函数部分处理,不使用着色器:
\(\begin{array}{c}
\text{裁切空间} \\
\small \mathbb{RP}^3
\end{array}
\xrightarrow[裁切]{\text{透视除法}}
\begin{array}{c}
\text{NDC} \\
\small [-1,1]^3
\end{array}
\xrightarrow[深度测试]{\text{视口变换}}
\begin{array}{c}
\text{屏幕空间} \\
\small [\![0, w]\!] \times [\![0, h]\!]
\end{array}\)
这一部分内容和选择的图形API密切相关,此处主要考虑的是 OpenGL 标准。
屏幕空间坐标可在片元着色器中通过gl_FragCoord.xy得到。
各个 API 对 NDC 坐标的定义总结如下:
| API 名 | XY 坐标范围 | X 轴正方向 | Y 轴正方向 | Z 坐标范围 |
|---|---|---|---|---|
| Vulkan | $[-1, 1]$ | 向右 | 向下 | $[0, 1]$ |
| OpenGL | $[-1, 1]$ | 向右 | 向上 | $[-1, 1]$ |
| DirectX 10 | $[-1, 1]$ | 向右 | 向上 | $[0, 1]$ |
由于深度缓冲的范围均是$[0,1]$,OpenGL 中从 NDC 到深度缓冲还需要额外进行变换。
比较棘手的部分是 Z 坐标的正方向。 这个正方向在图形 API 一般具有人为约定的意义,比如朝向显示器内(靠近相机)或朝向显示器外(远离相机),但实际上真正决定该方向的是进行深度测试时的比较函数。 若进行深度测试时选择比较函数为小于,那么深度更小的图元就会被绘制到屏幕上,从这个意义上,这意味这 Z 轴正方向朝向显示器内。
在渲染管线中,实际上唯一重要的是帧缓冲的坐标系方向,对应屏幕空间窗口坐标系的方向。 这个方向基本上是无法修改的,而 NDC 坐标系实际上均可随意修改。 注意虽然一般帧缓冲可作为纹理操作,但是帧缓冲坐标可能和纹理坐标不同。
| API 名 | U 轴正方向 | V 轴正方向 | 帧缓冲 X 轴正方向 | 帧缓冲 Y 轴正方向 |
|---|---|---|---|---|
| Vulkan | 无关1 | 无关 | 向右 | 向下 |
| OpenGL | 向右 | 向上 | 向右 | 向上 |
| DirectX 10 | 向右 | 向下 | 向右 | 向上 |
网上能看到一些资料宣称 OpenGL 的坐标系是左手系或者右手系,这是因为 OpenGL 默认的深度比较函数是小于(GL_LESS)。
实际上 OpenGL 标准中写道:
OpenGL does not force left- or right-handedness on any of its coordinates systems. 2
而对于 Vulkan 这种显式 API,由于没有默认的深度比较函数, Z 坐标就没有默认的正方向。 甚至还可通过设置负的视口大小来反转 Y 轴正方向。