GAMES101 课程 3、4 记录 & 作业 1 题解

2022 年 7 月 28 日 星期四(已编辑)
/ ,
12

阅读此文章之前,你可能需要首先阅读以下的文章才能更好的理解上下文。

GAMES101 课程 3、4 记录 & 作业 1 题解

这两节的内容主要是变换,包括建模时的缩放、平移、旋转都属于变换,而当物体在三维空间中处于世界坐标系下,摄像机去观察的时候,我们需要从世界坐标系将物体变换到相机坐标系中,最后当物体最后呈现在显示器上时,又需要进行投影变换,因此,变换也是及其基础的两节内容。

Lecture 3 变换【二维与三维】

一、变换类型

  • 模型变换【Modeling】
    • 缩放【scale】
    • 平移【Translation】
    • 旋转【Rotation】
    • 切变【Shear】
  • 视图变换【Viewing】
    • 3D 转 2D 的投影【Projection】

二、二维变换

这里的二维变换除了平移都是利用矩阵进行线性变换,做变换的时候我喜欢理解为,改变了物体所在空间坐标系的基造成了图像的变换,也就是基变换,忘了的话可以在 3Blue1Brown 的这期视频【线性代数的本质 - P13 基变换】重新回忆一下。

  • 缩放【Scale】
    非等比缩放

    非等比缩放
    假设组成左图的每个像素坐标为 (x,y)(x, y),组成右图的每个像素坐标为 (x,y)(x', y'),其中 x=sx×x,y=sy×yx' = s_x \times x, y' = s_y \times y,转化为矩阵形式则能得到这样的表示: \begin{bmatrix} x' \ y' \end{bmatrix} = \begin{bmatrix} s_x & 0 \ 0 & s_y \end{bmatrix} \begin{bmatrix} x \ y \end{bmatrix}
  • 镜像【Reflection】

    缩放得到镜像

    缩放得到镜像
    图像对应某个轴翻转也就是对应像素坐标都进行变号,这里是对于 yy 轴的镜像,于是可以得到: 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】

    切变:想象是把 y 轴往右边拉了以后整个图像的偏移

    切变:想象是把 y 轴往右边拉了以后整个图像的偏移
    假设组成左图的每个像素坐标为 (x,y)(x, y),组成右图的每个像素坐标为 (x,y)(x', y'),右图中偏转角度为 θ\theta,则可以得到: \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} 同理如果是偏转 xx 方向的话: \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}

  • 旋转【Rotation】

    旋转的矩阵推导

    旋转的矩阵推导
    假设组成左图的每个像素坐标为 (x,y)(x, y),组成右图的每个像素坐标为 (x,y)(x', y'),同时左图中左上角和右下角的点分别为 (0,1)(0,1)(1,0)(1, 0),这里同样可以从基变换的角度去理解,基于原点的旋转就是将整个基向量进行旋转移动,解旋转矩阵可通过以下方式: \begin{bmatrix} x' \ y' \end{bmatrix} = \begin{bmatrix} a & b \ c & d \end{bmatrix} \begin{bmatrix} x \ y \end{bmatrix} 将左图的点 (sinθ,cosθ)(0,1)(-\sin \theta, \cos \theta) \rightarrow (0, 1)(cosθ,sinθ)(1,0)(\cos \theta, \sin \theta) \rightarrow (1, 0) 带入上式 \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θR_\theta R_\theta = \begin{bmatrix} \cos \theta & -\sin \theta \ \sin \theta & \cos \theta \end{bmatrix}

以上都是线性变换,都可以用以下矩阵形式表达:

{x=ax+byy=cx+dy \left \{ \begin{aligned} x' & = ax + by \\ y' & = cx + dy \end{aligned} \right.
[xy]=[abcd][xy] \begin{bmatrix} x' \\ y' \end{bmatrix} = \begin{bmatrix} a & b \\ c & d \end{bmatrix} \begin{bmatrix} x \\ y \end{bmatrix}
x=Mx x' = Mx
  • 平移【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}

    显然这样的形式和前面的线性变换不一致,为了方便平移这类非线性变换也能使用 x=Mxx' = Mx 的方式表示,我们引入齐次坐标的概念。

三、齐次坐标【Homogeneous Coordinates】

给二维的向量和点增加一个维度,这样就可以表示所有的变换了。

  • 2D Point = (x,y,1)T(x, y, 1)^T
  • 2D Vector = (x,y,0)T(x, y, 0)^T

对应的平移可表示为

(xyw)=(10tx01ty001)(xy1)=(x+txy+ty1) \begin{pmatrix} x' \\ y' \\ w' \end{pmatrix} = \begin{pmatrix} 1 & 0 & t_x \\ 0 & 1 & t_y \\ 0 & 0 & 1 \end{pmatrix} \begin{pmatrix} x \\ y \\ 1 \end{pmatrix} = \begin{pmatrix} x + t_x \\ y + t_y \\ 1 \end{pmatrix}

对于点的齐次表示是增加 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】

逆变换就是反向变换,比如旋转一个角度,逆变换就是反着旋转回去,只需要左乘变换矩阵的逆矩阵就行,写作 x=M1xx' = M^{-1}x其中旋转矩阵是正交矩阵,旋转矩阵的转置就是他的逆矩阵

组合变换【Composing Transforms】

如何通过变换得到这样一个的图像

如何通过变换得到这样一个的图像

一个变换可以通过不同的多个基本变换组合得到,比如上图的变换可以通过先旋转再平移得到,写作:V=MtransMrotateVV' = M_{trans} * M_{rotate} * V

先旋转再平移

先旋转再平移

注意:矩阵乘法有结合律但是没有交换律,因此交换变换顺序得到的图像大概率都是不同的。

四、三维变换

三维变换和二维变换类似,这里做一个简单记录:

  • 三维向量:(x,y,z,0)T(x, y, z, 0)^T
  • 三维点:(x,y,z,1)T(x, y, z, 1)^T(x,y,z,w)T=(xw,yw,zw,1)T(x, y, z, w)^T = (\frac{x}{w}, \frac{y}{w}, \frac{z}{w}, 1)^T
  • 缩放 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}
  • 旋转
    • 关于 xx 轴旋转 α\alpha 角度 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}
    • 关于 yy 轴旋转 α\alpha 角度 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}
    • 关于 zz 轴旋转 α\alpha 角度 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}
    • 关于任意过原点的向量旋转

      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}

于是一个三维的旋转可以被看作是一个基于 x,y,zx, y, z 轴的旋转的组合,也被叫做欧拉角

Rxyz(α,β,γ)=Rx(α)Ry(β)Rz(γ) R_{xyz}(\alpha, \beta, \gamma) = R_x(\alpha)R_y(\beta)R_z(\gamma)

常被用于飞行模拟:Roll, Pitch, Yaw

飞行模拟中的 Roll,Pitch,Yaw

飞行模拟中的 Roll,Pitch,Yaw

Lecture 4 变换【模型、视图、投影】

观测变换是为了将三维空间中的物体变为二维,观测变换又分为视图变换和投影变换。

想象一下拍照的过程【MVP】:

  • 模型变换【Model Transformation】:找到一个好的角度,摆放模型
  • 视图变换【View Transformation】:找到一个号的角度设定相机
  • 投影变换【Projection Transformation】:按下快门,将三维场景投影到二维相片上

一、视图变换【View / Camera Transformation】

视图变换就是把相机放在空间中某个位置来观察物体,所以这个问题可以转换为如何定义一个摄像机。如何定义一个相机,有三个部分:

  • 相机位置【Position】:e\vec{e}
  • 相机观测方向【Gaze Direction】:g^\hat{g}
  • 相机头顶方向【Up Direction】:t^\hat{t}
相机位置

相机位置

通过相机空间观察物体,当相机和全部物体都在同时移动时是保持相对静止的,所以最后的成像也不会有改变,于是为了简便可以有两种方式:

  1. 以相机原点为坐标原点,变换所有的物体
  2. 以物体为原点,变换相机

课程里使用的是第一种方法,将相机固定在原点,朝向 z-z 轴,并让上方向为 yy 轴方向。

相对位置

相对位置

将相机从任意位置移动到原点只需要几次仿射变换:

  • 平移 e\vec{e} 到原点 (x,y,z)(-x, -y, -z)
  • 旋转 g^\hat{g}z-z
  • 旋转 t^\hat{t}yy
  • 旋转 (g^×t^)(\hat{g} \times \hat{t})xx
将相机移动到原点

将相机移动到原点

用矩阵表示整个变换过程就是 Mview=RviewTviewM_{view} = R_{view}T_{view}

  • 平移 e\vec{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}
  • 旋转将三个轴与 x,y,zx, y, z 轴对齐:这里比较麻烦的就在于,将轴旋转回原点是很复杂的,但是我们知道,旋转矩阵是一个正交矩阵,只需要先将坐标轴旋转到向量然后求逆矩阵【因为是正交矩阵所以结果就是转置】就可以得到旋转矩阵: 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】

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

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

正交投影【右】和透视投影【左】
  • 透视投影:成像效果更接近人眼,有近大远小的效果。
  • 正交投影:假设视点无限远,看到物体的视野范围是一个长方体,物体的属性在成像上不会受到视角影响,常用于工程制图。

正交投影

正交投影的一种简单的理解方式:

  • 相机位于原点看向 z-z 方向,上方向为 yy 轴方向
  • zz 轴丢弃
  • 将所有的矩形通过平移和缩放规范化到 [1,1]2[-1, 1]^2
简单理解正交投影

简单理解正交投影

正交投影的实现和上面的理解会略微不同,比如实现中正交投影就是把一个长方体 [l,r]×[b,t]×[f,n][l, r] \times [b, t] \times [f, n] 构成的空间压缩到一个 [1,1]3[-1, 1]^3 的立方体空间中。

正交投影的实现方式

正交投影的实现方式

注:l=left,r=right,b=bottom,t=top,f=far,n=nearl = left,r = right,b = bottom,t = top,f = far,n = near,其中因为相机朝向是 z-z 所以 n>fn > f

具体实现需要以下步骤:

  1. 把立方体空间平移到原点【空间中心与原点重合】
  2. 把空间缩放成 [1,1]3[-1, 1]^3

可以用矩阵的方式写作:

Mortho=SorthoTortho M_{ortho} = S_{ortho} \cdot T_{ortho}

空间中立方体的中心点通过定义的六个方向值就可以得到 (r+l2,t+b2,n+f2)(\frac{r + l}{2}, \frac{t + b}{2}, \frac{n + f}{2}),也就是:

Tortho=[100r+l2010t+b2001n+f20001] T_{ortho} = \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}

然后是缩放空间,也就是按各个边长和最后规范化构成的比例缩放,因此 x,y,zx, y, z 方向的缩放因子为:

Sx=2rlSy=2tbSz=2nf S_x = \frac{2}{r - l} \\ S_y = \frac{2}{t - b} \\ S_z = \frac{2}{n - f}

则缩放矩阵为:

Tortho=[2rl00002tb00002nf00001] T_{ortho} = \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}

最后可得到正交投影矩阵为:

Mortho=[2rl00r+lrl02tb0t+btb002nfn+fnf0001] M_{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}

透视投影

透视投影和正交投影类似,步骤为:

  1. 把空间平移到原点
  2. 将截锥体空间压缩成长方体【压缩距离较远的平面】
  3. 将空间缩放为 [1,1]3[-1, 1]^3 的立方体【正交投影】
压缩截锥体空间为立方体空间

压缩截锥体空间为立方体空间

相比正交投影,透视投影有一个把锥体变换成立方体的步骤,我们可以把这个步骤写作 MpersporthoM_{persp \rightarrow ortho},我们先了解这个变换的原理才能写出计算的方法:

截锥体转换到立方体的坐标计算方式

截锥体转换到立方体的坐标计算方式

上图是 YZYZ 平面的截面, nn 代表立方体的 nearnearff 代表 farfar 面,通过,图中的点 (x,y,z)(x, y, z) 在通过变换后会变为 (x,y,z)(x, y', z),我们可以写出如下等式:

nz=yy \frac{n}{z} = \frac{y'}{y}
y=nzy y' = \frac{n}{z}y

同理对于 xx 我们也可以得到 x=nzxx' = \frac{n}{z}x,但是 zz 方向不同,在透视变换的压缩中,点的 zz 方向会发生一定程度的变化,因此在这里是 unknownunknown 的状态,然后我们可以得到这样的关系:

Mpersportho(xyz1)=(nxznyzunknown1)=(nxnyunknownz) M_{persp \rightarrow ortho} \begin{pmatrix} x \\ y \\ z \\ 1 \end{pmatrix} = \begin{pmatrix} \frac{nx}{z} \\ \frac{ny}{z} \\ unknown \\ 1 \end{pmatrix} = \begin{pmatrix} nx \\ ny \\ unknown \\ z \end{pmatrix}

对于 xx 轴,由 ax+by+cz+d=nxax + by + cz + d = nx 得到,a=na = n 其余 b,c,db,c,d 都为 00,同理求 y,zy, z 轴可得:

Mpersportho=(n0000n00????0010) M_{persp \rightarrow ortho} = \begin{pmatrix} n & 0 & 0 & 0 \\ 0 & n & 0 & 0 \\ ? & ? & ? & ? \\ 0 & 0 & 1 & 0 \end{pmatrix}

第三行表示的是 zz 方向的变化,其中我们知道最近点最远中心点zz 轴在变化后也是最近点最远中心点,所以可以写出这样的关系【近平面任何点变化前后都不变,远平面只有中心点是不会变的】:

  • 假设近平面的任意点都不会变: \begin{pmatrix} x \ y \ n \ 1 \end{pmatrix} \Rightarrow \begin{pmatrix} nx \ ny \ n^2 \ n \end{pmatrix}

    同样带入 ax+by+cn+d=n2ax + by + cn + d = n^2 计算,因为结果与 x,yx, y 无关,所以可以假设第三行的四个元素为 (00AB)\begin{pmatrix}0 & 0 & A & B\end{pmatrix}

    (00AB)(xyn1)=n2 \begin{pmatrix} 0 & 0 & A & B \end{pmatrix} \begin{pmatrix} x \\ y \\ n \\ 1 \end{pmatrix} = n^2
  • 同理对于远平面的中心点 \begin{pmatrix} x \ y \ f \ 1 \end{pmatrix} \Rightarrow \begin{pmatrix} 0 \ 0 \ f^2 \ f \end{pmatrix}

    (00AB)(00f1)=f2 \begin{pmatrix} 0 & 0 & A & B \end{pmatrix} \begin{pmatrix} 0 \\ 0 \\ f \\ 1 \end{pmatrix} = f^2

联立上面的方程组可以得到:

{An+B=n2Af+B=f2{A=n+fB=nf \left \{ \begin{aligned} An + B & = n^2 \\ Af + B & = f^2 \end{aligned} \right. \Rightarrow \left \{ \begin{aligned} A & = n + f \\ B & = -nf \end{aligned} \right.

于是整个压缩变换矩阵就是:

Mpersportho=(n0000n0000n+fnf0010) M_{persp \rightarrow ortho} = \begin{pmatrix} n & 0 & 0 & 0 \\ 0 & n & 0 & 0 \\ 0 & 0 & n + f & -nf \\ 0 & 0 & 1 & 0 \end{pmatrix}

所以整个透视变换的矩阵形式就是 Mpersp=MorthoMpersporthoM_{persp} = M_{ortho}M_{persp \rightarrow ortho}

通常来说,在实际的工程中,我们通常并不会直接得到正交投影中的 [l,r][l, r][b,t][b, t] 的值,但是可以通过一些其他已经定义的东西计算出来,也就是 Field of view【FOV】Aspect ratio【屏幕宽高比】

FOV 和屏幕宽高比

FOV 和屏幕宽高比

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

用 FovY 和宽高比得到 l,r,b,t 值

用 FovY 和宽高比得到 l,r,b,t 值

以 FovY 举例,根据三角函数我们可以写出:

tanFovY2=tn \tan \frac{FovY}{2} = \frac{t}{|n|}

根据宽高比的定义,我们可以得到:

aspect=widthheight=2r2t=rt aspect = \frac{width}{height} = \frac{2r}{2t} = \frac{r}{t}

联立方程可以得到:

t=ntanFovY2r=aspecttb=tl=r t = |n|\tan \frac{FovY}{2} \\ r = aspect * t \\ b = -t \\ l = -r

这样就得到了所有需要的值。

作业 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_fovaspect_ratiozNearzFar 可以得到 l, r, t, b 的值就可以直接构建得到。
渲染得到的结果

渲染得到的结果

使用社交账号登录

  • Loading...
  • Loading...
  • Loading...
  • Loading...
  • Loading...