Skip to content

Lec_1

更新: 12/9/2025 字数: 0 字 时长: 0 分钟

the quick brown fox jumps over the lazy dog.

THE QUICK BROWN FOX JUMPS OVER THE LAZY DOG.

课程主页:https://sites.cs.ucsb.edu/~lingqi/teaching/games101.html

闫令琪老师推荐的书:Steve Marschner and Peter Shirley, Fundamentals of Computer Graphics。第三版或更新版本。

Overview

Course Topic (mainly 4 parts):

  • Rasterization 光栅化

    把三维空间的几何形体显示在屏幕上,就是光栅化。实时和离线的区分是30fps。

  • Curves and Meshes

  • Ray Tracing

  • Animation / Simulation

GAMES101 is NOT about:

  • Using OpenGL / DirectX / Vulkan
  • The syntax of Shaders
  • 在这节课后,你就能够自己把它们学起来。

此外,不会讲计算机视觉(需要推测图形)的内容,因为不属于计算机图形学的范畴。

但随着技术发展,实际上两者的界限越来越模糊,交叉越来越多。

null

null

Assignments:

  • with provided code skeleton and virtual machine image
  • weekly (usually no more than 20 lines of code)
  • Language: C++

作业非强制,纯练手。

Website: http://www.smartchair.org/GAMES2020Coures-YLQ/

Review of Linear Algebra

图形学所依赖:

  • Basic Math ——线性代数,微积分,统计学

  • Basic Physics—— 经典力学,光学

  • 其他——信号处理,数学分析

  • 一点美感

Vectors

向量可以表现一个方向。ex: v=(1,2,3)

单位向量表示一个方向上模长为1的向量:v^=v|v|

在图形学上,向量默认一般是列向量。ex:

v=[123]

向量的转置是将行向量和列向量互换。ex:

vT=[123]

在笛卡尔系(Cartesian Coordinate System)中,向量的长度的计算非常简单:

|v|=12+22+32=14

向量的加法就是简单的分量相加,而乘法则有两种方式:点乘和叉乘。

点乘(内积,dot product)

  • 点乘的结果是一个标量(scalar):ab=a1b1+a2b2+a3b3
  • 也可以表示为:ab=|a||b|cosθ
  • cosθ=ab|a||b| & cosθ=a^b^

点乘在图形学中最重要的应用就是计算两个向量的夹角。

点乘也满足诸多性质:

  • 交换律:ab=ba
  • 分配律:a(b+c)=ab+ac
  • 结合律:(ka)b=k(ab)=a(kb)

点乘可以用来计算投影(projection)。ex:

  • b: projection of b on a
  • b=ka^
  • k=b=bcosθ

投影的好处在于可以让我们分解一个向量。ex: b=b+(bb)

TIP

对于向量,|v||v 都可以表示向量的模长。

点乘还可以用来判断方向。ex:

  • ab>0 ,则夹角: θ<90
  • ab<0 ,则夹角: θ>90
  • ab=0 ,则夹角: θ=90

叉乘(外积,cross product)

  • 叉乘的结果是一个向量:a×b=|i^j^k^a1a2a3b1b2b3|

  • a×b=b×a

  • 大小关系:||a×b||=||a||||b||sinθ

叉乘的结果垂直于两个向量所张成的平面,其方向可以使用右手法则(right-hand rule)来确定。

ex:令食指垂直于大拇指和中指,右手大拇指指向 a 方向,中指指向 b 方向,则食指指向 a×b 方向。

利用叉乘,可以在三维空间中搭建一个正交坐标系(orthonormal basis)。

  • 右手系: x×y=z
  • 左手系: x×y=z

满足一些性质:

  • 分配律:a×(b+c)=a×b+a×c
  • 结合律:(ka)×b=k(a×b)=a×(kb)
  • a×a=0
  • a×kb=k(a×b)
  • a×b=(b×a)

两个列向量的叉乘可以表示为一个行列式(determinant):

a×b=|i^j^k^a1a2a3b1b2b3|=(a2b3a3b2,a3b1a1b3,a1b2a2b1)

叉乘的应用:

  • 计算左和右

    ex: a×b 结果为正,说明 ba 的左边;反之亦然。

  • 计算内和外

    ex: 对于三角形ABC(逆时针),有一点 p,如果 AB×APBC×BPCA×CP 都是正的,则 p 在三角形内;反之亦然。

Matrices

矩阵就是一组数的二维数组。ex:

A=[123456789]

矩阵最重要的操作就是乘法。但可以相乘的矩阵必须满足一定条件:第一个矩阵的列数等于第二个矩阵的行数。

ex: Nx3矩阵可以和3xM矩阵相乘,结果是一个NxM矩阵。

结果矩阵的每个元素是通过第一个矩阵的行向量和第二个矩阵的列向量的点乘得到的。ex:

C=ABcij=k=1naikbkj

ex:

[1234][5678910]=[15+2816+2917+21035+4836+4937+410]=[212427475461]

矩阵乘法满足的一些性质:

  • 不满足交换律:ABBA
  • 满足结合律:A(BC)=(AB)C
  • 满足分配律:A(B+C)=AB+AC

矩阵可以和向量相乘,结果是一个新的向量。ex:

Av=[123456789][123]=[11+22+3341+52+6371+82+93]=[143250]

矩阵的转置是将行和列互换。ex:

AT=[147258369]

矩阵的转置的乘积满足一个特殊的性质:

(AB)T=BTAT

单位矩阵(identity matrix)是一个对角矩阵,对角线上的元素为1,其余元素为0。ex:

I=[100010001]

单位矩阵的作用类似于数字1,对于任何矩阵 A,都有 AI=IA=A

通过单位矩阵,我们可以定义矩阵的逆(inverse)。

AA1=A1A=I

矩阵的逆的乘积满足一个特殊的性质:

(AB)1=B1A1

其实,我们可以把向量的相乘也看作是矩阵的乘法。

  • 点乘:

    ab=aTb=[a1a2a3][b1b2b3]=(a1b1+a2b2+a3b3)
  • 叉乘:

    把a转化为一个反对称矩阵(skew-symmetric matrix)A,然后和b相乘。

    a×b=Ab=[0a3a2a30a1a2a10][b1b2b3]

Transformations

Scale Matrix (缩放矩阵)

在 x 轴上缩放0.5,在 y 轴上缩放2:

S=[0.5002]

令一个向量 v=[xy],则缩放后的结果为:

Sv=[0.5002][xy]=[0.5x2y]

Shear Matrix (错切矩阵)

切变矩阵可以将一个矩形变成一个平行四边形。

ex:

H=[1k01]

v 经过切变后的结果为:

Hv=[1k01][xy]=[x+kyy]

对于结果生成的平行四边形,每个点点 y 轴不变,但 x 轴会根据 y 的值进行平移。

Rotation Matrix (旋转矩阵)

我们将一个矩形,旋转(默认逆时针旋转,绕原点)一个角度 θ,则旋转矩阵为:

R=[cosθsinθsinθcosθ]
推导

我们考虑一个单位正方形,四个顶点分别为 (1,0), (0,1), (-1,0), (0,-1),将旋转 θ 角度后。

先考虑 (1,0) 点,旋转后变为 (cosθ,sinθ) 。因此:

[cosθsinθ]=[ABCD][10]

计算矩阵叉积,可得:

A=cosθ,C=sinθ

同理我们带入点 (0,1),可得 :

B=sinθ,D=cosθ

旋转矩阵是正交的(orthogonal),即一个矩阵的转置等于其逆矩阵:RT=R1

我们可以简单地验证一下:

(RRT)=[cosθsinθsinθcosθ][cosθsinθsinθcosθ]=[1001]

Liner Transformation (线性变换)

先前提到的缩放,切变,旋转矩阵都属于线性变换。

对于线性变化后的 x 和 y 坐标,可以表示为:

x=ax+byy=cx+dy

可以视作一个矩阵和向量的乘积:

[xy]=[abcd][xy]

Affine Transformation (仿射变换)

仿射变换是线性变换的推广,允许平移操作。

x=ax+by+t1y=cx+dy+t2

可以表示为:

[xy]=[abcd][xy]+[t1t2]

可见仿射变换不属于先前提到的线性变换。不过平移本身上一个非常简单的操作,我们不想将其复杂化。于是,我们引入齐次坐标(homogeneous coordinates)。

Homogeneous Coordinates (齐次坐标)

齐次坐标的引入是为了将仿射变换表示为矩阵乘法。

通过齐次坐标转化:

  • 2D 点:(x,y)(x,y,1)
  • 2D 向量:(x,y)(x,y,0)

表示平移:

[xyw]=[10t101t2001][xy1]=[x+t1y+t21]

注意到向量和点的区别,向量的齐次坐标最后一位是0,因此平移矩阵对向量没有影响,此为向量方向不变的体现。

在齐次坐标中,点和向量的加减:

  • vector + vector = vector
  • point + vector = point
  • point - point = vector
  • point + point = ???

一般来说,点点相加没有意义。但在齐次坐标中,我们将相加结果除以 w,能够得到中点。

注意到,在先前的变化中:

[xy]=[abcd][xy]+[t1t2]

我们可以轻松地将其转化为齐次坐标形式(注意a, b, c, d):

[xy1]=[abt1cdt2001][xy1]

TIP

在二维的仿射变化中,齐次坐标的最后一位始终为0..1。但在其他变换中,可能有所不同。

Inverse Transformation (逆变换)

将一个变换恢复到原始状态,即求逆变换。

恰巧,逆变换的矩阵刚好是原变换矩阵的逆矩阵。

毕竟:

MM1[xy1]=I[xy1]=[xy1]

Composing Transformations (复合变换)

一个复杂的变换可以由多个简单变组合而成,其变换的顺序操作是固定的(不满足交换律)。

操作的顺序从右到左排列(A1 -> A2 -> A3):

A3A2A1[xy1]

将多个变换矩阵相乘,能得到一个新的变换矩阵, 表示一个复杂的变换。

ex:先在 x 轴上平移1个单位,再旋转45度。

T=[101010001][cos45sin450sin45cos450001]=[2222122220001]

Decomposing Complex Transformations (分解复杂变换)

一个复杂的变换可以分解为多个简单变换的乘积。

ex:先前提到,旋转默认是围绕原点的。那如果我们想围绕某个点 (xc,yc) 旋转该怎么办?

我们可以将其分解为三个步骤:

  1. 平移点 (xc,yc) 到原点
  2. 围绕原点旋转 θ 角度
  3. 将点平移回点 (xc,yc)

3D Transformations

在三维空间中的,依旧需要将向量转化为齐次坐标:

  • 3D 点:(x,y,z)(x,y,z,1)
  • 3D 向量:(x,y,z)(x,y,z,0)

使用齐次坐标表示变化:

[xyz1]=[abct1deft2ghit30001][xyz1]

在三维空间中,最复杂的操作就是操作可能就是旋转了。对于任意的旋转可能很难写,我们写先虑绕 x, y, z 轴的旋转。

  1. 绕 x 轴旋转 θ 角度:
Rx=[10000cosθsinθ00sinθcosθ00001]
  1. 绕 y 轴旋转 θ 角度:
Ry=[cosθ0sinθ00100sinθ0cosθ00001]
  1. 绕 z 轴旋转 θ 角度:
Rz=[cosθsinθ00sinθcosθ0000100001]

TIP

注意到此处只有绕 y 轴旋转时 sinθ 前面是正号,其他两个轴都是负号。这是因为循环对称性: x×y=zy×z=xz×x=y 而不是 x×z

考虑任意一个旋转,我们都可以将其拆解为为绕 x, y, z 轴旋转的复合变换:

R(α,β,γ)=Rx(α)Ry(β)Rz(γ)

Rodrigues' Rotation Formula (罗德里格斯旋转公式)

给定一个旋转角度 θ 和一个单位旋转轴 u^=(ux,uy,uz),则绕该轴旋转 θ 角度的旋转矩阵为:

R(n,α)=cosθI+(1cosθ)u^u^T+sinθ[0uzuyuz0uxuyux0]

当提到沿某个旋转轴旋转时,一般默认这个旋转轴是自原点出发的,此公式便是据于此。如果非要沿某个非原点的轴旋转,则可以先将旋转轴和图形平移到原点,旋转后再平移回去。

sinθ 后方的矩阵是什么

假设有两个向量 ab 进行叉乘,我们可以将其中一个向量转化为一个矩阵,称为反对称矩阵(skew-symmetric matrix):

a×b=Ab=[0a3a2a30a1a2a10][b1b2b3]

所以可以看出 sinθ 后方的矩阵其实就是旋转轴 u^ 的反对称矩阵。

此外还有四元数(quaternion)表示旋转的方法,更善于做插值(interpolation),但不在本课程范围内。

Viewing Transformation

View / Camera Transformation

想象拍一张照片:

  1. 找个好地方安置好人 (model transformation)
  2. 调整好相机位置 (view transformation)
  3. 拍下照片 (projection transformation)

在图形学中,把三维空间转化到二维世界,过程就像拍一张照片,在图形学中叫做 MVP 变换(Model-View-Projection Transformation)。

在进行 View Transformation 时,我们先定义一个相机:

  • Position (位置):e
  • Look-at direction (观察方向):g
  • Up direction (上方向):t —— 摄像机头顶指向的方向。

约定俗成:

  • 将摄像机永远放置于原点,看向 -z 方向,上方向为 y 轴正方向
  • 相机永远不动,只有其他物体在动

将摄像机移动到原点,需要进行一系列的变换:

  1. 平移摄像机位置 e 到原点
  2. 旋转摄像机,使得观察方向 g 对其 -z
  3. 旋转摄像机,使得上方向 t 对齐 y

写成一个矩阵形式:

Mview=RviewTview

其中 :

Tview=[100xe010ye001ze0001]

旋转矩阵不好求解,我们可以考虑其逆变换,比如将 (0,0,1) 旋转到 g 方向,这就相对好求解了。

我们需要一个矩阵,可以旋转 gztyg×tx

Rview1=[xg×txtxg0yg×tytyg0zg×tztzg00001]

由于旋转矩阵是正交矩阵,所以 Rview=(Rview1)T

Rview=[xg×tyg×tzg×t0xtytzt0xgygzg00001]

如此一来,我们变完成了 Camera/View Transformation。

Projection Transformation

投影有两种不同方式:

  • Orthographic Projection (正交投影)
  • Perspective Projection (透视投影)

正交投影一般用在工程图中,物体投影到二维平面,其形状不变。高中数学题中的几何图形采用的就是正交投影,长度相同的线段在远处和近处看起来也是相同长度。

透视投影更符合人类的视觉感知,远处的物体看起来更小。ex: 火车轨道看起来在远处最终会汇聚在一点。

PrespectivePv&OrthographicP

INFO

道理我都懂,但是鸽子为什么这么大?

Orthographic Projection (正交投影)

我们定义一个空间中的立方体 [xmin,xmax]×[ymin,ymax]×[zmin,zmax],试图将其映射到 canonical (正则/标准/规范) cube [1,1]3 中。

我们先将立方体平移到原点,然后再缩放到 [1,1]3,该过程可以写成两个矩阵的乘积:

M=[2xmaxxmin00002ymaxymin00002zmaxzmin00001][100xmin+xmax2010ymin+ymax2001zmin+zmax20001]

由于相机的观察方向为 -z 轴,所以其实近大于远,即越靠近物体的 z 值越大。而如果采用右手系,则会产生远大于近的效果,这是一种更直观的方式,也是为什么像 OpenGL 采用右手系的原因。

TIP

物体确实会被拉伸,但之后还会在 Viewport Transformation (视口变换) 再进行一次拉伸以调整。

Perspective Projection (透视投影)

透视投影是应用更加广泛,更符合人类视觉感知,也相对复杂一些的投影方式。在透视投影中:

  • 物体近大远小
  • 平行线会汇聚到一个点(消失点,vanishing point)

先回忆一下齐次坐标,对于点 (x,y,z,1),在所有坐标乘上 w 后,仍代表同一个点 (wx,wy,wz,w)

ex: (1,0,0,1)(2,0,0,2) 都表示同一个点。

在透视投影中,我们从一个点(相机位置)出发,向外延伸出一个视锥体(frustum)。视锥体有两个平面,分别是近平面(near clipping plane)和远平面(far clipping plane)。我们要做的,就是将远平面映射到近平面上。

Frustum&Cuboid

投射投影矩阵非常复杂,很多教材都会直接给出结果矩阵,但不好理解。我们给出一个更好理解的方式。

考虑远平面的那四个点往里挤,挤到和近平面一样大,这样远处的点只要做一个正交投影就可以投影到近平面上。“挤” 的描述很模糊,我们先来规定这个“挤”所必须遵循的规则:

  • 近平面在“挤”后不会变化
  • 远平面上点的 z 值在“挤”后不会改变
  • 远屏幕中心点在“挤”后位置不会改变,即仍在远平面中心

那我们接下来理清一下任意一个点 (x,y,z) 在“挤”后的位置 应该如何计算。首先我们从这个 frustum 的侧面来看,其中 (x,y,z) 是近平面上的点,n 是近平面到相机的距离:

Frustum侧面

(x,y,z) 最后会移动到和 (x,y,z) 同一个高度(yafter=y)。此外,我们很容易看出来近平面分割出了一对相似三角形。根据相似三角形的性质,我们可以得到:

y=nzy

从而我们得到了任意一个点的 y 值会被如何挤压,同理,得到 xx 的关系:

x=nzx

那现在我们就得到了任意点 xy 会如何变化的,对于 z 值,我们暂时还不知道。不过我们是可以先写出变化后的齐次坐标:

(xyz1)(nxznyzunknown1)mult.by z==(nxnystill unknownz)

通过目前推导信息,我们已经可以写出投影矩阵的大部分内容了。投影矩阵 Mpersportho 应该满足:

Mpersportho(4×4)(xyz1)=(nxnyunknownz)

很直观的,这个矩阵大致是这样:

Mpersportho=(n0000n00????0010)

接下来唯一剩下的难题就是这个矩阵的第三行了,但我们实际只知道任意点 xy 会在挤压后如何变化,并不清楚任意点的 z 值在投影后会会如何变化。我们只清楚近平面和远平面上的点 z 值会不会变化,而这也就足够了,我们把这两个关键信息利用起来。

根据近平面点不变:

Mpersportho(4×4)(xyz1)=(nxnyunknownz)replace z with n(xyn1)(xyn1)==(nxnyn2n)

所以,矩阵的第三行一定满足 (0 0 A B) 的形式:

(00AB)(xyn1)=n2An+B=n2

接下来我们利用一下远平面点不变的性质。远平面中心的点 (0,0,f,1) 在变化任然是 (0,0,f,1) 上,同样道理:

(00f1)(00f1)==(00f2f)Af+B=f2

联立两个结论可得 AB

{An+B=n2Af+B=f2{A=f+nB=nf

终于啊终于,我们得以窥见投影矩阵的全貌:

Mpersportho=(n0000n0000f+nnf0010)

这应该可能是整节课设计到最复杂的推导了,如果你能理解的话那真的很了不起哦,希望我能讲清楚吧 QAQ。

至于在变化后,中间点的 z 值会如何变化,我邀请读者们把它作为一个小练习。