跳过正文
  1. Posts/

GAMES101学习笔记04:变换(模型.视图,投影)

··4713 字·10 分钟·
Xenolies
作者
Xenolies
Keep On Keeping On

MVP变换(Model-View-Projection Matrices)
#

MVP变换用来描述视图变换的任务,即将虚拟世界中的三维物体映射(变换)到二维坐标中。

MVP变换分为三步:

  • 模型变换(model tranformation):将模型空间转换到世界空间(找个好的地方,把所有人集合在一起,摆个pose)
  • 摄像机变换(view tranformation):将世界空间转换到观察空间(找到一个放相机的位置,往某一个角度去看)
  • 投影变换(projection tranformation):将观察空间转换到裁剪空间(茄子!)

视图变换(View)
#

视图变换的目的是变换Camera位置到原点,上方为Y,观察方向为-Z

  • Camera的y轴正方向向上,z轴方向是\(\vec{x} \times \vec{y}\)右手系)

  • 对物体进行运动,摄像机会跟随着一起运动保持相对位置不变

  • 怎么写出数学表示

通过\(V_{\text{view}}\)​变换相机,在数学上怎么表示?

  • 将e平移到零点
  • 旋转g到−Z
  • 旋转t到Y
  • 旋转G x T到X

可以将其拆解成两个部分: 平移+旋转

$$M_{VIEW} = R_{VIEW} T_{VIEW}$$

首先将相机从e平移到标准原点的位置

$$ T_{VIEW} = \begin{bmatrix} 1 & 0 & 0 & -x_e \\ 0 & 1 & 0 & -y_e \\ 0 & 0 & 1 & -z_e \\ 0 & 0 & 0 & 1 \end{bmatrix} $$

由于旋转矩阵具有正交性,因此它的逆矩阵就等于它的转置矩阵.

$$ \begin{bmatrix} 1 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & 1 & 0 \\ 0 & 0 & 0 & 1 \end{bmatrix} \cdot \begin{bmatrix} x_{\hat{g}\times\hat{t}} & x_t & x_{-g} & 0 \\ y_{\hat{g}\times\hat{t}} & y_t & y_{-g} & 0 \\ z_{\hat{g}\times\hat{t}} & z_t & z_{-g} & 0 \\ 0 & 0 & 0 & 1 \end{bmatrix} = E \cdot \begin{bmatrix} x_{\hat{g}\times\hat{t}} & x_t & x_{-g} & 0 \\ y_{\hat{g}\times\hat{t}} & y_t & y_{-g} & 0 \\ z_{\hat{g}\times\hat{t}} & z_t & z_{-g} & 0 \\ 0 & 0 & 0 & 1 \end{bmatrix} $$

在通过正交矩阵的性质(正交矩阵的逆等于它的转置矩阵),可以求出:

$$ R_{\text{view}} = \begin{bmatrix} x_{\hat{g} \times \hat{t}} & y_{\hat{g} \times \hat{t}} & z_{\hat{g} \times \hat{t}} & 0 \\ x_t & y_t & z_t & 0 \\ x_{-g} & y_{-g} & z_{-g} & 0 \\ 0 & 0 & 0 & 1 \end{bmatrix} $$

总结:

  • 让物体和相机一起变换。
  • 直到相机位于原点,头朝上(Y),看向 −Z

投影变换(Projection)
#

投影变换分为两种:

  • 正交投影(Orthogonal projection)变换:透视线平行
  • 透视投影(Perspective projection)变换:透视线相交,近大远小

简单来讲,透视投影相当于两点透视或三点透视,有透视畸变。正交投影相当于是基于坐标的轴测图

正交投影 (Orthogonal projection)
#

一种简单的理解方式

  • 相机位于原点,朝向 -Z 方向,向上为 Y 方向

  • 丢弃 Z 坐标

  • 将得到的矩形平移并缩放到 [-1, 1]² 范围内

  • 我们希望将一个长方体区域 [l, r] × [b, t] × [f, n] (左右 X 下上 X 远近)

  • 映射到立方体 [-1, 1]³

方法:

  1. 现将标准立方体拉到原点
  2. 后将x,y,z轴各自**伸缩到[−1,1]

变换顺序:先移动(中点移动到原点),再缩放(基向量缩放比例为\(\frac{2}{\text{长} / \text{宽} / \text{高}}\)

因为相机朝向-z方向(右手系),此时n > f,这也是 OpenGL 用左手系( f > n )的原因。

正交投影矩阵(Orthographic Projection Matrix)
#

$$ M_{\text{ortho}} = \underbrace{ \begin{bmatrix} \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{bmatrix} }_{\text{缩放矩阵 } S} \cdot \underbrace{ \begin{bmatrix} 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{bmatrix} }_{\text{平移矩阵 } T} $$

合并后形式

$$ M_{\text{ortho}} = \begin{bmatrix} \frac{2}{r-l} & 0 & 0 & -\frac{r+l}{r-l} \\ 0 & \frac{2}{t-b} & 0 & -\frac{t+b}{t-b} \\ 0 & 0 & \frac{2}{n-f} & -\frac{n+f}{n-f} \\ 0 & 0 & 0 & 1 \end{bmatrix} $$

参数说明:

  • l, r → 左右边界(left / right)
  • b, t → 下上边界(bottom / top)
  • f, n → 远近裁剪面(far / near),注意在右手系中通常 n > f (如 n = -0.1, f = -100)

透视投影 (Perspective projection)
#

  • 透视投影是应用最广泛的投影。
  • 近大远小。
  • 平行线看上去不再平行。

 回顾:齐次坐标的性质

  • (x, y, z, 1)、(kx, ky, kz, k ≠ 0)、(xz, yz, z², z ≠ 0) 都表示三维空间中的同一个点 (x, y, z)
  • 例如:(1, 0, 0, 1) 和 (2, 0, 0, 2) 都表示点 (1, 0, 0)

简单,但很有用

如何做透视投影
#

更好理解的方式是想象原屏幕上的四个顶点,我们把他们挤到和近平面同一个高度上。换言之,我们把他“挤”成了空间中的一个长方体。然后做正交投影就行了。

在挤压的操作中我们规定几点:

  • 近平面上的点永远不变(想象你要把窗户外面的东西画到窗户上,窗户上的东西不会改变)。
  • 远平面的点z值不会发生变化。
  • 远平面的中心点不会变。

研究挤压:

从侧面观察挤压过程

根据相似三角形,可以得出xx'yy'的对应关系:

$$ y' = \frac{n}{z}y, \quad x' = \frac{n}{z}x $$

原本的坐标根据齐次坐标表示会变成(第二步同乘以 z)

$$ \begin{bmatrix} x \\ y \\ z \\ 1 \end{bmatrix} \rightarrow \begin{bmatrix} nx/z \\ ny/z \\ \text{unknown} \\ 1 \end{bmatrix} \overset{\text{mult. by } z}{==}\begin{bmatrix} nx \\ ny \\ z \cdot \text{unknown} \\ z \end{bmatrix} $$

把这个变换用齐次坐标矩阵表示:

$$ M_{(4 \times 4)} \begin{bmatrix} x \\ y \\ z \\ 1 \end{bmatrix} == \begin{bmatrix} nx \\ ny \\ z \cdot \text{unknown} \\ z \end{bmatrix} $$

根据矩阵乘法规则,我们可以反推出矩阵 M 的结构如下:

$$ M_{persp \to ortho} = \begin{pmatrix} n & 0 & 0 & 0 \\ 0 & n & 0 & 0 \\ ? & ? & ? & ? \\ 0 & 0 & 1 & 0 \end{pmatrix} $$

投影 == 挤压 x y + 正交投影,look at -z 的时候,z 上的坐标被干掉了,所以投影变换前后 z 的坐标不变

第三行负责计算变换后的 z 坐标(z’)

  • 近裁剪平面上的任意点,其 z 值不会改变
  • 远裁剪平面上的任意点,其 z 值也不会改变

我们再利用下面两条性质就可以得到未知的部分

  • 近平面上的点不变
  • 远平面中心点不变
  1. 条件一:近平面上的任意点(令unknown=n,z=n): $$ M \cdot P_{near} = \begin{pmatrix} n & 0 & 0 & 0 \\ 0 & n & 0 & 0 \\ 0 & 0 & A & B \\ 0 & 0 & 1 & 0 \end{pmatrix} \begin{bmatrix} x \\ y \\ n \\ 1 \end{bmatrix} = \begin{bmatrix} nx \\ ny \\ An + B \\ n \end{bmatrix} $$

因为不涉及旋转,所以第三行与x,y无关。

$$ \text{第三行运算结果:} \quad \begin{bmatrix} 0 & 0 & A & B \end{bmatrix} \cdot \begin{bmatrix} x \\ y \\ n \\ 1 \end{bmatrix} = n^2 $$

即:

$$ An + B = n^2 \tag{1} $$
  1. 条件二:远平面中心点不变 远平面的中心点满足 x=0,y=0,z=f:
$$ P_{far\_center} = \begin{bmatrix} 0 \\ 0 \\ f \\ 1 \end{bmatrix} $$

将其乘以矩阵 M ,关注结果向量的第三分量(即变换后的 z′):

$$ M \cdot P_{far\_center} = \begin{pmatrix} n & 0 & 0 & 0 \\ 0 & n & 0 & 0 \\ 0 & 0 & A & B \\ 0 & 0 & 1 & 0 \end{pmatrix} \begin{bmatrix} 0 \\ 0 \\ f \\ 1 \end{bmatrix} = \begin{bmatrix} 0 \\ 0 \\ Af + B \\ f \end{bmatrix} $$

同理可得:

$$ \text{第三行运算结果:} \quad \begin{bmatrix} 0 & 0 & A & B \end{bmatrix} \cdot \begin{bmatrix} 0 \\ 0 \\ f \\ 1 \end{bmatrix} = f^2 $$

即:

$$ Af + B = f^2 \tag{2} $$

求解方程组 因此可以得到(1)(2)两个关系式,联立方程 (1) 和 (2)::

$$\begin{cases} An + B = n^2 \\ Af + B = f^2 \end{cases}$$
  1. 第一步:消元求解 A 用方程 (2) 减去方程 (1):
$$A(f - n) = f^2 - n^2$$

利用平方差公式展开右边:

$$A(f - n) = (f - n)(f + n)$$

解得 A :

$$ A = f + n $$
  1. 第二步:代入求解 B 将 A=f+n 代入方程 (1):
$$(f + n)n + B = n^2$$

展开并化简:

$$fn + n^2 + B = n^2$$

解得 B

$$B = -fn$$

最终得到:

$$ \begin{cases} A = n + f \\ B = -nf \end{cases} $$

求得变换矩阵为:

$$ M_{\text{persp} \to \text{ortho}} = \begin{bmatrix} n & 0 & 0 & 0 \\ 0 & n & 0 & 0 \\ 0 & 0 & n + f & -nf \\ 0 & 0 & 1 & 0 \end{bmatrix} $$

透视投影数学表示和总结
#

总结归纳下:

透视投影的本质可以总结为 “先变形,后归一化” 的两步走策略。

想象你在拍一张照片:

  1. 挤压:先把眼前立体的风景,通过某种魔法压扁成一张平面的底片(此时还没透视感,只是数据变了)。
  2. 正交:把这张底片裁剪成标准尺寸。
  3. 除法:当你透过镜头看这张底片时,大脑(GPU)自动把它还原成立体的画面,远的就远,近的就近。

🎯 核心逻辑:两步变换

  1. 挤压变形 (Shear & Scale)

    • 动作:把“金字塔形”的视锥体(近小远大),强行挤压成一个“长方体”。
    • 目的:让原本复杂的透视关系,变成简单的正交关系。
    • 关键操作:利用齐次坐标 w 分量,让 zz值影响 x,yx,y 的缩放(即 x′=x⋅n zx′=x⋅zn​ ),实现“近大远小”的数学基础。
  2. 正交投影 (Orthographic)

    • 动作:对挤压后的长方体进行标准的平移和缩放。
    • 目的:将长方体映射到标准设备坐标空间 (NDC, 即 [−1,1]3[−1,1]3 )。
    • 结果:此时所有物体都变成了正交视图,但深度信息 ( zz ) 已被特殊编码保留。
  3. 透视除法 (Perspective Division) —— 隐式步骤

    • 动作:GPU 自动执行 (x,y,z)/w(x,y,z)/w 。
    • 效果:因为第一步中 ww被设为了 z ,除以 ww 后,远处的点坐标变小,近处的点坐标变大,透视效果正式显现

投影矩阵的变换 最终透视投影矩阵 = 正交投影矩阵 × 透视转正交矩阵

$$ M_{\text{persp}} = M_{\text{ortho}} \cdot M_{\text{persp} \to \text{ortho}} $$

整理后就是:

$$ M_{\text{persp}} = \underbrace{ \begin{bmatrix} \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{bmatrix} }_{\text{缩放矩阵 } S} \cdot \underbrace{ \begin{bmatrix} 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{bmatrix} }_{\text{平移矩阵 } T} \cdot \underbrace{ \begin{bmatrix} n & 0 & 0 & 0 \\ 0 & n & 0 & 0 \\ 0 & 0 & n+f & -nf \\ 0 & 0 & 1 & 0 \end{bmatrix} }_{M_{\text{persp} \to \text{ortho}}} $$

拆解说明:

这实际上是说:

标准正交投影矩阵 M_ortho = 缩放矩阵 S × 平移矩阵 T

也就是说,作者把原本一个复杂的 M_ortho 拆成了两个更直观的几何操作:

  1. 平移 T:将视景体中心移到原点
    → 对应平移量:(-(r+l)/2, -(t+b)/2, -(n+f)/2)

  2. 缩放 S:将视景体缩放到 [-1,1]³
    → 对应缩放因子:(2/(r-l), 2/(t-b), 2/(n-f))

虽然图中保留了完整分解,但在实际编程中(如 OpenGL),我们通常使用简化后的单矩阵版本

$$ M_{\text{persp}} = \begin{bmatrix} \frac{2n}{r-l} & 0 & \frac{l+r}{l-r} & 0 \\ 0 & \frac{2n}{t-b} & \frac{b+t}{b-t} & 0 \\ 0 & 0 & \frac{n+f}{n-f} & \frac{2fn}{f-n} \\ 0 & 0 & 1 & 0 \end{bmatrix} $$

视口变换
#

经过上述MVP变换以后,所有物体都在 [-1,1]^3 的cube中,下一步需要将他们花在屏幕上,这个过程就是光栅化

视锥
#

视锥表示看起来像顶部切割后平行于底部的金字塔的实体形状。这是透视摄像机可以看到和渲染的区域的形状。

定义视锥:

  • 长宽比 Aspect
  • 垂直的角度 FovY

利用视锥来获取物体的长宽高

需要注意的是:

  • 近平面总高度 = 2t
  • 近平面总宽度 = 2r

公式解析

  1. 计算半高度t $$ \tan\left(\frac{\text{fovY}}{2}\right) = \frac{t}{|n|} $$
  • fovY / 2: 这是垂直视野角的一半。如上图所示,它构成了一个直角三角形的一个锐角。
  • |n|: 这是近平面距离(near plane distance),即从摄像机原点 (0,0,0) 到近平面的距离。因为通常使用右手坐标系且摄像机朝向-Z轴,所以 n 是一个负数,这里取绝对值 |n| 作为三角形的邻边长度。
  • t: 这是我们要求的对边长度,即从中心线到近平面上边缘的距离。

利用三角函数里面的正切函数求解(tan = 对边/邻边),可以得到t:

$$ t = |n| \cdot \tan\left(\frac{\text{fovY}}{2}\right) $$

现在有了摄像机的近平面距离 |n| 和 垂直视野角 fovY,就可以精确计算出近平面的半高 t

  1. 计算半宽度r $$ \text{aspect} = \frac{r}{t} $$
  • aspect: 这是屏幕或图像的宽高比,通常是已知的(例如 16:9 的屏幕,aspect = 16/9 ≈ 1.778)。
  • r: 这是我们要计算的,即从中心线到近平面右边缘的距离(半宽)。
  • t: 这是我们刚刚通过第一个公式计算出来的半高。

利用宽高比定义(宽高比 = 总宽 / 总高)将公式变形:

$$ r = t \cdot \text{aspect} $$

现在算出半高 t 并且知道宽高比 aspect 的情况下,可以计算出近平面的半宽 r

屏幕(Screen)
#

  • 二维数组,数组元素为像素,分辨率 = 数组尺寸
  • 属于光栅显示器,典型的光栅成像设备

光栅(Raster)
#

  • “Raster” 在德语中就是 “screen” 的意思。
  • (将图形画在屏幕上) 光栅化” 就是把几何图形(三角形、线条等)绘制到屏幕上的过程,即把矢量/连续空间的数据转换为离散的像素值。

像素(Pixel <- Picture element)
#

  • 像素是一个颜色均匀的小正方形
  • 颜色混合而成(红、绿、蓝)

屏幕空间(Screen space)
#

认为屏幕左下角是原点,向右是x,向上是y

规定:

  1. 像素的索引形式为 (x, y),其中 x 和 y 均为整数
  2. 像素的索引范围是从 (0, 0) 到 (width - 1, height - 1)
  3. 像素 (x, y) 的中心位于 (x + 0.5, y + 0.5)
  4. 屏幕覆盖的范围是从 (0, 0) 到 (width, height)

视口变换公式
#

做的事情:
先不考虑z轴,把MVP后处于标准立方体映射到屏幕上。即

$$ [-1, 1]^2 \to [0, \text{width}] \times [0, \text{height}] $$$$ M_{\text{viewport}} = \begin{bmatrix} \frac{\text{width}}{2} & 0 & 0 & \frac{\text{width}}{2} \\ 0 & \frac{\text{height}}{2} & 0 & \frac{\text{height}}{2} \\ 0 & 0 & 1 & 0 \\ 0 & 0 & 0 & 1 \end{bmatrix} $$

把虚拟世界中的任意物体顶点,最终映射到屏幕像素坐标上:

$$ M_{\text{total}} = M_{\text{viewport}} \cdot M_{\text{projection}} \cdot M_{\text{view}} \cdot M_{\text{model}} $$

注意:矩阵乘法不满足交换律! 必须按“模型 → 视图 → 投影 → 视口”的顺序相乘

步骤矩阵名作用输入空间 → 输出空间
1.模型变换M_model将物体从本地坐标系移到世界坐标系模型空间 → 世界空间
2️.视图变换M_view将世界坐标系转换为以相机为中心的坐标系世界空间 → 相机空间
3️.投影变换M_projection将 3D 场景投影到 2D 平面,并归一化到 NDC相机空间 → 裁剪空间 → NDC
4️.视口变换M_viewport将 NDC 映射到实际屏幕像素坐标NDC → 屏幕空间