背景
矩阵与向量
在图形学中,我们使用矩阵来进行一系列坐标处理,包括物体的平移、旋转、缩放,以及物体从三维空间到二维平面的转化。这些处理的数学基础是矩阵和向量的乘法。这里可以把 $n$ 维的列向量看作 $n$ 行一列的矩阵,使用矩阵乘法的逻辑和同样是 $n$ 行的矩阵相乘。这样可以得到一个新的 $n$ 行一列矩阵,即一个新的 $n$ 维的列向量(如公式 1.1 所示)。由此我们可以使用矩阵将一个向量转变成另一个向量。在坐标变换处理中,我们可以将点的坐标表示为列向量,并用矩阵进行坐标变换。这里我们使用右手坐标系,即 x 向左,y 向上,z 向外。
齐次坐标(Homogeneous Coordinates)
通常情况下,一个图形由若干个顶点组成。当我们需要对图形进行处理时,我们会将矩阵乘以图形的每一个顶点,从而算出这些这些顶点表示的图形的新位置。在笛卡尔坐标系(直角坐标系)中,一个三维的点可以表示为 $(x, y, z)$。在这个坐标系中,我们可以很方便的使用矩阵乘法对图形进行线性变换如缩放或旋转。但非线性变换如平移变换只能通过加减法解决。这些变换的具体实现方式将后文中介绍。
为了用统一的矩阵表示旋转、缩放、平移,我们引入齐次坐标系来表示点的坐标。齐次坐标系相比笛卡尔坐标系添加了一个维度。以三维坐标为例,齐次坐标系的 $(x, y, z, w)$ 对应笛卡尔坐标系的坐标 $(x/w, y/w, z/w)$。 当 $w$ 为 1 时,齐次坐标的 $x, y, z$ 直接对应笛卡尔坐标的 $x, y, z$。$w$ 为 $0$ 时,齐次坐标 $(x, y, z, w)$ 相当于表示无穷远的点,即方向为 $(x, y, z)$ 的向量。
MVP 模型
MVP 模型用三个矩阵,即模型(model)矩阵,视图(view)矩阵,投影(projection)矩阵来计算我们最终可以看到的所有物体及其位置。这其中,模型(model)矩阵是针对物体的变换,包括平移、缩放、旋转等。每个物体有其自己的视图矩阵,将其以合适的大小摆放在合适的位置,并给出其世界坐标。视图(view)矩阵是针对观察点的变换,它将所有的模型的坐标从世界坐标转为相机坐标,即将物体的位置对应到以相机为原点,以观察方向为轴的坐标系的对应坐标上。投影(projection)矩阵则是限定视野范围,并将视野范围内的物体随视野范围按比例缩放成 $[-1. 1]^3$ 的立方体。注意 MVP 模型不包括 3D 转 2D 的变换。
模型变换(Model Transformation)
在 MVP 模型中,模型变换指用平移、旋转、缩放等各种方式转化点、向量等场景中实体,将模型摆放在我们需要的位置。首先我们需要将已知的笛卡尔坐标系的坐标转为齐次坐标系的坐标,即添加一个数值为 $1$ 的 $w$ 维度。然后使用变换矩阵对其进行处理。
平移(Translation)
假设我们需要将点 $(x, y, z)$ 的 $x, y, z$ 分别平移 $(t_x, t_y, t_z)$,得到点 $(x + t_x, y + t_y, z + t_z)$,公式 2.1 展示了如何使用矩阵进行平移变换,以及其计算结果结果。
缩放(Scaling)
假设我们需要将点 $(x, y, z)$ 的 $x, y, z$ 分别缩放 $(s_x, s_y, s_z)$,得到点 $(x s_x, y s_y, z * s_z)$,公式 2.2 展示了如何使用矩阵进行缩放变换,以及其计算结果结果。
旋转(Rotation)
旋转的计算相对复杂。通常情况下我们会将其拆解成绕 $x$ 轴旋转、绕 $y$ 轴旋转、绕 $z$ 轴旋转这三个不同的矩阵。这些矩阵可以通过特殊值(如$(0, 0, 1, 1)$)代入的方式求出。公式 2.3 展示了我们如何用矩阵将点 $(x, y, z)$ 绕 $x$ 轴旋转 $\phi$ 度。
公式 2.4 则展示了点绕 $y$ 轴、 $z$ 轴旋转 $\phi$ 度所需要的旋转矩阵。
除了绕轴旋转,我们还可以使用一些其他模型比如欧拉角或四元数来表示物体旋转。这些旋转模型可以通过一定公式转化为矩阵。我们可以先利用这些模型计算旋转,再将其转为矩阵,比应用在点和物体上。
错切(Shearing)
错切是一种特殊的变换,它会根据点离某一轴的距离在该轴方向上平移不同的大小,从而使物体的发生形变。通常情况下,当我们需要将场景或物体扭曲,造成迷幻的效果时,我们会使用错切变换。错切变换可以只沿一个轴错切,也可以沿两个轴一起错切。对于错切矩阵,我们将单位值移动量(坐标为 1 的点的偏移量)放在单位矩阵的 $i$ 行 $j$ 列。其中 $i$ 表示点的移动方向轴,$j$ 表示点的偏移基准。公式 2.5 表示物体沿 $x$ 轴向远离/接近 $z$ 轴错切,单位移动量为 $s$。
对于三维错切,在两个轴方向上的错切可以同时进行。公式 2.6 表示物体沿 $x$ 轴、$y$ 轴分别向远离/接近 $z$ 轴方向错切 $s$、$t$。
变换顺序
当我们进行一系列基本变换时,变换顺序会对最终结果影响。比如,如果先进行平移,原点会移出物体中心,再进行缩放或者旋转时这个变换就不再是以物体为中心进行的了。因此通常情况下,我们一般遵循先缩放,再旋转,最后平移的顺序。而错切则根据需要放在对应的步骤之后。当然这个顺序不是绝对顺序,有时也可以利用这个相互影响来实现一些功能。
在整个变换中,我们将矩阵乘在向量的左边。因此当我们需要在原有的基础上添加一个变换时,我们应该将新的矩阵乘在原来的式子的左边。因此,每个模型的模型矩阵等于将该模型变换到指定位置的所有矩阵的按序左乘的结果。
视图变换(View\Camera Transformation)
在实际应用中,用户的观察角度,即相机位置,并不一定在原点位置。在不同的位置以不同的角度观察,物体位置、遮挡顺序等都会不同。而此前计算的物体位置都是相对原点的位置。因此我们需要视图变换来计算在一定位置上以一定角度观察摆放好的物体所得到的场景。视图矩阵就是计算物体相对相机的位置的矩阵。
想要计算视图矩阵,我们需要知道相机的坐标以及相机的方向。而相机的方向又包括视角的朝向以及对上下的定义。基于运动的相对性,移动相机约等于从反方向移动物体。因此为了方便后续投影的计算,我们将相机固定在原点,并以 $y$ 轴为上往 $-z$ 方向看。视图矩阵就是可以将相机移动回原点的并旋转到规定位置的矩阵。我们将视图矩阵作用在所有物体上,就可以得到物体相对相机的位置,即在相机所在的点和方向上看到的场景。
在具体的变换中,为了避免旋转对位置的影响,我们首先将相机移动到原点,再进行旋转调整轴的方向。由于向量将乘在矩阵右侧,移动矩阵作为先进行的变换应该放在右侧。公式 3.1 表示了视图矩阵的计算方式。
视图移动矩阵
假设相机位置为 $(x_e, y_e, z_e)$,参考前文对模型做平移变换,可知相机视图移动矩阵为公式 3.2 所示。
视图旋转矩阵
假设我们所看向的视角方向为 $\vec{g}$,而向上的方向为 $\vec{t}$。因此视图旋转矩阵的作用应该是将 $\vec{g}$ 转到 $-z$ 方向,将 $\vec{t}$ 转到 $y$ 方向,将 $(t \times g)$ 转到 x 方向。
由于将视角方向旋转到坐标轴的矩阵和将坐标轴转到视角方向的矩阵互逆,且将视角方向旋转到坐标轴的矩阵非常难求。在实际求解中,我们可以先将坐标轴转到视角方向的矩阵,再求其逆矩阵。旋转矩阵可以通过特殊值(如 $x$ 轴的齐次坐标 $(1, 0, 0, 0)$ 等)解出。这个旋转矩阵是一个正交矩阵,因此它的逆矩阵就是转置矩阵。公式 3.3 展示了将视角方向旋转到坐标轴的矩阵和将坐标轴转到视角方向的矩阵。
投影变换(Projection Transformation)
投影变换截取三维空间中的可视六面体范围,并将该六面体和其内部所有物体按比例拉伸或收缩到到边长为 1 的立方体内,以方便后续三维转二维的变换。主要的投影变换有两种:正交投影和透视投影。正交投影不遵循近大远小的规律,原来平行的线变换后还是平行。透视投影遵循近大远小的规律,原来平行的线投影后会相交。透视投影更接近人眼真实的感受,因此更为常用,而正交投影多用于工程制图等场景。下图展示了透视投影和正交投影的几何效果。
在视图变换中,我们已经将相机移动至原点,并且向 $-z$ 方向看,以 $y$ 为上方。因此当我们定义上下($(t, b)$)、左右($(l, r)$)、远近($(n, f)$)时,数组值上通常 $t > b, r > l, n > f$。
正交投影(Orthographic Projection)
正交投影近处的平面大小和远处的平面大小完全一致,可视范围是一个长方体。首先定义正交投影的长方体的上下($(t, b)$)、左右($(l, r)$)、远近($(n, f)$)。为了将它映射到一个以原点为中心的标准的 $[-1, 1]^3$ 立方体上,我们首先用平移矩阵将中心点移到原点,再用缩放矩阵将边长拉伸成 $[-1, 1]$。公式 4.1 展示了正交投影矩阵的获得公式。
透视投影(Perspective Projection)
这里我们使用 GAMES101[1] 中的方法获得透视投影矩阵,即先将透视投影的远平面压缩至和近平面同样的大小,再用和正交投影同样的方法进行转换。此外也有一些其他方式可以直接求出投影矩阵[3]。要计算透视投影,我们首先需要知道可视范围的远近($n, f$),以及近平面的左右($l, r$)和上下($t, b$)。如果用视角范围(Field of View,FOV)表示,垂直张角为 $\theta$,宽高比 $a = r / t$。此时 $t$ 和 $b$ 为 $\pm n tan(\theta/2)$,$l$ 和 $r$ 为 $\pm n tan(\theta / 2) * a$。可视角张角越小,远近平面大小区别越小,透视投影越接近正交投影。
要获得透视投影的范围,我们首先连出一个四棱锥。四棱锥以原点为顶点,以近平面为底。延长四棱锥的四条边,并根据规定的远处距离截取远平面。在计算时,我们先将远平面压缩成和近平面相同的长方体(远平面的 $z$ 坐标不变,中心不变,大小变为和近平面相同),再做正交投影,如公式 4.2 所示。
接下来我们开始计算压缩远平面的矩阵,即 $M_{persp->ortho}$。由相似三角形可知,在可视范围内任意一点,其变换后的 $x, y$ 坐标 $x’, y’$ 与其位置 $z$ 呈公式 4.3 所展示的关系。由于齐次坐标的特殊性(即 $w$ 坐标),将齐次坐标的所有坐标乘以相同的数值,在笛卡尔坐标系上所表示的点不变。因此为了消除计算中的变量,即点的 $z$ 坐标,我们将该点所有坐标都乘以 $z$。
此时我们可以得到变换矩阵的 $x$、$y$、$w$ 三个维度,即获得公式 4.4 的矩阵。
接着我们计算 $z$ 坐标的变化。由于在近平面和远平面上 $z$ 坐标不变,且 $z$ 坐标与 $x$, $y$ 坐标无关。因此,代入近平面的中心点和远平面的中心点,可得公式 4.5 所示的两个方程。
通过公式 4.6 可以得到 $A, B$ 的值。
因此最后得出的转换矩阵为公式 4.7 所示。
通过特殊值代入可知,点的 $z$ 坐标并非不变,而是会向远离观察点的方向移动。
视口变换(Screen Mapping)
视口变换是所有矩阵变换的最后一步,即将三维空间转成二维平面。此前我们得到的是 $[-1, 1]^3$ 的正方体,但屏幕通常并非正方形(多为长方形,如 1920*1080)。此外,屏幕空间的原点一般也不设在中心,而是左下角,同时向右为 $x$ 轴正方向,向上为 $y$ 轴正方向。这样所有的像素坐标都是正值,且每个像素大小为 $1 * 1$,坐标为整数且指代像素点左下角。此处不考虑光栅化(即像素化)内容。同样,$z$ 坐标表示物体的前后关系,用于判断遮挡,此处也暂时不考虑。因此,我们只需要考虑 $x, y$ 两个坐标。先将正方体的 $x$ 大小从 $2$ 拉成屏幕宽度 $w$,将 $y$ 大小从 $2$ 拉成屏幕高度 $h$,再将左下角平移至原点,即 $x$ 平移 $+w/2$,$y$ 平移 $+h/2$。公式 5.1 表示了该变换的变换矩阵。直接去除 $z$ 坐标,就可以得到物体在二维空间中的坐标。