GAMES101 课程 3、4 记录 & 作业 1 题解
这两节的内容主要是变换,包括建模时的缩放、平移、旋转都属于变换,而当物体在三维空间中处于世界坐标系下,摄像机去观察的时候,我们需要从世界坐标系将物体变换到相机坐标系中,最后当物体最后呈现在显示器上时,又需要进行投影变换,因此,变换也是及其基础的两节内容。
Lecture 3 变换【二维与三维】
一、变换类型
- 模型变换【Modeling】
- 缩放【scale】
- 平移【Translation】
- 旋转【Rotation】
- 切变【Shear】
- 视图变换【Viewing】
- 3D 转 2D 的投影【Projection】
二、二维变换
这里的二维变换除了平移都是利用矩阵进行线性变换,做变换的时候我喜欢理解为,改变了物体所在空间坐标系的基造成了图像的变换,也就是基变换,忘了的话可以在 3Blue1Brown 的这期视频【线性代数的本质 - P13 基变换】重新回忆一下。
- 缩放【Scale】
假设组成左图的每个像素坐标为 ,组成右图的每个像素坐标为 ,其中 ,转化为矩阵形式则能得到这样的表示: \begin{bmatrix} x' \ y' \end{bmatrix} = \begin{bmatrix} s_x & 0 \ 0 & s_y \end{bmatrix} \begin{bmatrix} x \ y \end{bmatrix}
非等比缩放 镜像【Reflection】
图像对应某个轴翻转也就是对应像素坐标都进行变号,这里是对于 轴的镜像,于是可以得到: x' = -x \ y' = y 将坐标以矩阵形式表达也就是 \begin{bmatrix} x' \ y' \end{bmatrix} = \begin{bmatrix} -1 & 0 \ 0 & 1 \end{bmatrix} \begin{bmatrix} x \ y \end{bmatrix}
缩放得到镜像切变【Shear】
假设组成左图的每个像素坐标为 ,组成右图的每个像素坐标为 ,右图中偏转角度为 ,则可以得到: \left { \begin{aligned} x' & = x + y \cdot \tan \theta \ y' & = y \end{aligned} \right. 将坐标以矩阵形式得到 \begin{bmatrix} x' \ y' \end{bmatrix} = \begin{bmatrix} 1 & a \ 0 & 1 \end{bmatrix} \begin{bmatrix} x \ y \end{bmatrix} 同理如果是偏转 方向的话: \left { \begin{aligned} x' & = x \ y' & = y + x \cdot \tan \theta \end{aligned} \right. \begin{bmatrix} x' \ y' \end{bmatrix} = \begin{bmatrix} 1 & 0 \ \tan \theta & 1 \end{bmatrix} \begin{bmatrix} x \ y \end{bmatrix}
切变:想象是把 y 轴往右边拉了以后整个图像的偏移旋转【Rotation】
假设组成左图的每个像素坐标为 ,组成右图的每个像素坐标为 ,同时左图中左上角和右下角的点分别为 和 ,这里同样可以从基变换的角度去理解,基于原点的旋转就是将整个基向量进行旋转移动,解旋转矩阵可通过以下方式: \begin{bmatrix} x' \ y' \end{bmatrix} = \begin{bmatrix} a & b \ c & d \end{bmatrix} \begin{bmatrix} x \ y \end{bmatrix} 将左图的点 和 带入上式 \left { \begin{aligned} -\sin \theta & = a \times 0 + b \times 1 \ \cos \theta & = c \times 0 + d \times 1 \ \cos \theta & = a \times 1 + b \times 0 \ \sin \theta & = c \times 1 + d \times 0 \end{aligned} \right. 于是可以得到旋转矩阵 为 R_\theta = \begin{bmatrix} \cos \theta & -\sin \theta \ \sin \theta & \cos \theta \end{bmatrix}
旋转的矩阵推导
以上都是线性变换,都可以用以下矩阵形式表达:
平移【Translation】
对于平移变换可这样描述坐标的变化: x' = x + t_x \ y' = y + t_y 转换为矩阵形式就是 \begin{bmatrix} x' \ y' \end{bmatrix} = \begin{bmatrix} a & b \ c & d \end{bmatrix} \begin{bmatrix} x \ y \end{bmatrix} + \begin{bmatrix} t_x \ t_y \end{bmatrix}
平移是非线性变换显然这样的形式和前面的线性变换不一致,为了方便平移这类非线性变换也能使用 的方式表示,我们引入齐次坐标的概念。
三、齐次坐标【Homogeneous Coordinates】
给二维的向量和点增加一个维度,这样就可以表示所有的变换了。
- 2D Point =
- 2D Vector =
对应的平移可表示为
对于点的齐次表示是增加 1,而向量是 0

点和向量的齐次坐标不同的原因
这样可以得到一个性质,两个点的坐标相加能够直接表示出两个点的中点。然后通过齐次坐标的形式,我们可以把仿射变换【Affine】都写成以下形式:
仿射变换【Affine】= 线性变换 + 平移 \begin{pmatrix} x' \ y' \ 1 \end{pmatrix} = \begin{pmatrix} a & b & t_x \ c & d & t_y \ 0 & 0 & 1 \end{pmatrix} \begin{pmatrix} x \ y \ 1 \end{pmatrix}
缩放【Scale】 S(s_x, s_y) = \begin{pmatrix} s_x & 0 & 0 \ 0 & s_y & 0 \ 0 & 0 & 1 \end{pmatrix}
旋转【Rotation】 R(\alpha) = \begin{pmatrix} \cos \theta & -\sin \theta & 0 \ \sin \theta & \cos \theta & 0 \ 0 & 0 & 1 \end{pmatrix}
平移【Translation】 T(t_x, t_y) = \begin{pmatrix} 1 & 0 & t_x \ 0 & 1 & t_y \ 0 & 0 & 1 \end{pmatrix}
逆变换【Inverse Transform】
逆变换就是反向变换,比如旋转一个角度,逆变换就是反着旋转回去,只需要左乘变换矩阵的逆矩阵就行,写作 。其中旋转矩阵是正交矩阵,旋转矩阵的转置就是他的逆矩阵。
组合变换【Composing Transforms】

如何通过变换得到这样一个的图像
一个变换可以通过不同的多个基本变换组合得到,比如上图的变换可以通过先旋转再平移得到,写作:

先旋转再平移
注意:矩阵乘法有结合律但是没有交换律,因此交换变换顺序得到的图像大概率都是不同的。
四、三维变换
三维变换和二维变换类似,这里做一个简单记录:
- 三维向量:
- 三维点: 或
- 缩放 S(s_x, s_y, s_z) = \begin{pmatrix} s_x & 0 & 0 & 0 \ 0 & s_y & 0 & 0 \ 0 & 0 & s_z & 0 \ 0 & 0 & 0 & 1 \ \end{pmatrix}
- 平移 T(t_x, t_y, t_z) = \begin{pmatrix} 1 & 0 & 0 & t_x \ 0 & 1 & 0 & t_y \ 0 & 0 & 1 & t_z \ 0 & 0 & 0 & 1 \end{pmatrix}
- 旋转
- 关于 轴旋转 角度 R_x(\alpha) = \begin{pmatrix} 1 & 0 & 0 & 0 \ 0 & \cos\alpha & -\sin\alpha & 0 \ 0 & \sin\alpha & \cos\alpha & 0 \ 0 & 0 & 0 & 1 \end{pmatrix}
- 关于 轴旋转 角度 R_x(\alpha) = \begin{pmatrix} \cos\alpha & -\sin\alpha & 0 & 0 \ \sin\alpha & \cos\alpha & 0 & 0 \ 0 & 0 & 1 & 0 \ 0 & 0 & 0 & 1 \end{pmatrix}
- 关于 轴旋转 角度 R_x(\alpha) = \begin{pmatrix} \cos\alpha & 0 & \sin\alpha & 0 \ 0 & 1 & 0 & 0 \ -\sin\alpha & 0 & \cos\alpha & 0 \ 0 & 0 & 0 & 1 \end{pmatrix}
关于任意过原点的向量旋转
- 罗德里格斯旋转公式【Rodrigues' rotation formula】
R = \cos\theta I + (1 - \cos\theta) nn^T + \sin\theta \begin{pmatrix} 0 & -n_z & n_y \ n_z & 0 & -n_x \ -n_y & n_x & 0 \end{pmatrix}
于是一个三维的旋转可以被看作是一个基于 轴的旋转的组合,也被叫做欧拉角:
常被用于飞行模拟:Roll, Pitch, Yaw

飞行模拟中的 Roll,Pitch,Yaw
Lecture 4 变换【模型、视图、投影】
观测变换是为了将三维空间中的物体变为二维,观测变换又分为视图变换和投影变换。
想象一下拍照的过程【MVP】:
- 模型变换【Model Transformation】:找到一个好的角度,摆放模型
- 视图变换【View Transformation】:找到一个号的角度设定相机
- 投影变换【Projection Transformation】:按下快门,将三维场景投影到二维相片上
一、视图变换【View / Camera Transformation】
视图变换就是把相机放在空间中某个位置来观察物体,所以这个问题可以转换为如何定义一个摄像机。如何定义一个相机,有三个部分:
- 相机位置【Position】:
- 相机观测方向【Gaze Direction】:
- 相机头顶方向【Up Direction】:

相机位置
通过相机空间观察物体,当相机和全部物体都在同时移动时是保持相对静止的,所以最后的成像也不会有改变,于是为了简便可以有两种方式:
- 以相机原点为坐标原点,变换所有的物体
- 以物体为原点,变换相机
课程里使用的是第一种方法,将相机固定在原点,朝向 轴,并让上方向为 轴方向。

相对位置
将相机从任意位置移动到原点只需要几次仿射变换:
- 平移 到原点
- 旋转 到 轴
- 旋转 到 轴
- 旋转 到 轴

将相机移动到原点
用矩阵表示整个变换过程就是 :
- 平移 到原点: 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}
- 旋转将三个轴与 轴对齐:这里比较麻烦的就在于,将轴旋转回原点是很复杂的,但是我们知道,旋转矩阵是一个正交矩阵,只需要先将坐标轴旋转到向量然后求逆矩阵【因为是正交矩阵所以结果就是转置】就可以得到旋转矩阵: R{view}^{-1} = \begin{bmatrix} x{\hat{g}\times\hat{t}} & xt & 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} \Rightarrow R{view} = \begin{bmatrix} x{\hat{g}\times\hat{t}} & y{\hat{g}\times\hat{t}} & z{\hat{g}\times\hat{t}} & 0 \ xt & y_t & z_t & 0 \ x{-g} & y{-g} & z{-g} & 0 \ 0 & 0 & 0 & 1 \end{bmatrix}
二、投影变换【Projection Transformation】
投影是将 3D 转为 2D 的过程,分为正交投影【Orthographic Projection】和透视投影【Perspective Projection】

正交投影【左】和透视投影【右】

正交投影【右】和透视投影【左】
- 透视投影:成像效果更接近人眼,有近大远小的效果。
- 正交投影:假设视点无限远,看到物体的视野范围是一个长方体,物体的属性在成像上不会受到视角影响,常用于工程制图。
正交投影
正交投影的一种简单的理解方式:
- 相机位于原点看向 方向,上方向为 轴方向
- 把 轴丢弃
- 将所有的矩形通过平移和缩放规范化到

简单理解正交投影
正交投影的实现和上面的理解会略微不同,比如实现中正交投影就是把一个长方体 构成的空间压缩到一个 的立方体空间中。

正交投影的实现方式
注:,其中因为相机朝向是 所以
具体实现需要以下步骤:
- 把立方体空间平移到原点【空间中心与原点重合】
- 把空间缩放成
可以用矩阵的方式写作:
空间中立方体的中心点通过定义的六个方向值就可以得到 ,也就是:
然后是缩放空间,也就是按各个边长和最后规范化构成的比例缩放,因此 方向的缩放因子为:
则缩放矩阵为:
最后可得到正交投影矩阵为:
透视投影
透视投影和正交投影类似,步骤为:
- 把空间平移到原点
- 将截锥体空间压缩成长方体【压缩距离较远的平面】
- 将空间缩放为 的立方体【正交投影】

压缩截锥体空间为立方体空间
相比正交投影,透视投影有一个把锥体变换成立方体的步骤,我们可以把这个步骤写作 ,我们先了解这个变换的原理才能写出计算的方法:

截锥体转换到立方体的坐标计算方式
上图是 平面的截面, 代表立方体的 面 代表 面,通过,图中的点 在通过变换后会变为 ,我们可以写出如下等式:
同理对于 我们也可以得到 ,但是 方向不同,在透视变换的压缩中,点的 方向会发生一定程度的变化,因此在这里是 的状态,然后我们可以得到这样的关系:
对于 轴,由 得到, 其余 都为 ,同理求 轴可得:
第三行表示的是 方向的变化,其中我们知道最近点和最远中心点的 轴在变化后也是最近点和最远中心点,所以可以写出这样的关系【近平面任何点变化前后都不变,远平面只有中心点是不会变的】:
假设近平面的任意点都不会变: \begin{pmatrix} x \ y \ n \ 1 \end{pmatrix} \Rightarrow \begin{pmatrix} nx \ ny \ n^2 \ n \end{pmatrix}
同样带入 计算,因为结果与 无关,所以可以假设第三行的四个元素为
同理对于远平面的中心点: \begin{pmatrix} x \ y \ f \ 1 \end{pmatrix} \Rightarrow \begin{pmatrix} 0 \ 0 \ f^2 \ f \end{pmatrix}
联立上面的方程组可以得到:
于是整个压缩变换矩阵就是:
所以整个透视变换的矩阵形式就是 。
通常来说,在实际的工程中,我们通常并不会直接得到正交投影中的 和 的值,但是可以通过一些其他已经定义的东西计算出来,也就是 Field of view【FOV】 和 Aspect ratio【屏幕宽高比】。

FOV 和屏幕宽高比
FOV 分为 FovY 和 FovX 两个部分,结合屏幕宽高比就可以将 和 算出来:

用 FovY 和宽高比得到 l,r,b,t 值
以 FovY 举例,根据三角函数我们可以写出:
根据宽高比的定义,我们可以得到:
联立方程可以得到:
这样就得到了所有需要的值。
作业 1 题解
对于作业题解,这里按照 Game101 的规范都不会记录相关代码,不过其实网上一搜也能搜到很多了,主要记录一个解题过程。
本次作业主要是使用 CPU 模拟一个光栅化渲染的过程,其中我们需要对 main.cpp
中的函数进行补全:
get_model_matrix(float rotation_angle)
:- 将输入的
rotation_angle
值换算成为弧度值,并创建一个旋转矩阵作为输出,交给set_model
函数传给光栅化器。
- 将输入的
get_projection_matrix(float eye_fov, float aspect_ratio, float zNear, float zFar)
:- 需要返回的是一个投影矩阵,根据输入的
eye_fov
,aspect_ratio
和zNear
,zFar
可以得到l, r, t, b
的值就可以直接构建得到。
- 需要返回的是一个投影矩阵,根据输入的

渲染得到的结果