---
title: "【CG】3D数学基础"
author: "Perrin Yong"
author_profile: https://www.pystone.net/profile/
published_by: "Perrin Yong"
canonical: https://www.pystone.net/notes/cg-3d-math-fundamentals/
type: note
content_role: unspecified
visibility: public
id_stability: rename-stable
source_path: "10-计算机、信息技术与工程/05-游戏图形与运行时/渲染基础/【CG】3D数学基础.md"
content_hash: a3d74a83bf80f8fde854845b731c697ea2952cc17d132fb4ebc670a858642c10
knowledge_version: 224c990773de.5fa8af6e39fa
site_commit: 224c990773de166d23a886306577dd90379529ce
notes_commit: 5fa8af6e39fa3891d1b9b4832bfa6c4e0ecaaf0a
---
# 【CG】3D数学基础

> 创建时间：2020/11/16 22:29

  * 【CG】3D数学基础
    * 坐标系与基
    * 矢量
      * 点积 / 内积 （dot product）/（innr product）
      * 叉积 / 外积 （cross product）/（outer product）
    * 矩阵
      * 特殊矩阵
        * 逆矩阵（inverse matrix）
        * 正交矩阵（orthogonal matrix）
      * 矩阵运算
      * 矩阵存储
      * 矩阵的几何意义 - 变换
        * 线性变换（linear transform）
        * 仿射变换（affine transform）
          * 平移
          * 缩放
          * 旋转
          * 复合
    * 矩阵与坐标空间变换
      * 坐标空间变换基础
      * 渲染管线中的坐标空间转换过程
        * 模型空间（model space） | 对象空间（object space） | 局部空间（local space）
        * 世界空间(world space)
        * 观察空间(view space) | 摄像机空间(camera space)
        * （齐次）裁剪空间（clip space）
          * 透视投影（perspective projection）
          * 正交投影（orthographic projection）
        * 屏幕空间（screen space）
          * 齐次除法（homogeneous division） | 透视除法（perspective division）
        * 屏幕空间（screen space）
          * 屏幕映射
      * 法线变换
    * 其他问题
    * Ref

## 坐标系与基

  * 笛卡儿坐标系（ Cartesian Coordinate System）

  * 基向量（basis vector）

  * 正交基（orthogonal basis）：基向量互相垂直

  * 标准正交基（orthonormal basis）：基向量互相垂直，长度为1

  * 左/右手坐标空间（left/right-handed coordinate space）

  * 旋向性（handedness）

> Unity坐标系：
>  左手坐标系 - 模型空间，世界空间
>  右手坐标系 - 观察空间，摄像机前向是z轴负方向，值越小，深度越大，越远

## 矢量

  * 标量（Scalar）

  * 矢量（vector）

    * 模（magnitude）

    * 方向（direction）

    * 头（head）尾（tail）

> 矢量表示相对于某个点的偏移（displacement）

  * 单位矢量（unit vector） / 被归一化的矢量（normalized vector）

  * 归一化（normalization） - 将非零矢量归一化的过程

> 零矢量不可归一化

### 点积 / 内积 （dot product）/（innr product）

几何意义 - 投影（projection）

![Alt text](/media/2f94c8a9d6a73c851332.png)

### 叉积 / 外积 （cross product）/（outer product）

  * a × b= −( b × a)

  * ｜ a × b ｜= ｜ a ｜ ｜ b ｜ sin θ

几何意义 - 平行四边形面积

## 矩阵

### 特殊矩阵

  * 方块矩阵（square matrix）

  * 对角元素（diagonal elements）

  * 对角矩阵（diagonal matrix）

  * 单位矩阵（identity matrix）

  * 转置矩阵（transposed matrix）

![Alt text](/media/7c252ae9c7dabae2fd9a.png)

![Alt text](/media/4a86f22a7052bb452249.png)

#### 逆矩阵（inverse matrix）

可逆的条件: 首先应该是方阵

![Alt text](/media/08f72c0d36c19ca14640.png)

  * 可逆的 / 非奇异的 （invertible / nonsingular）

  * 不可逆的 / 奇异的 （noninvertible / singular）

行列式（determinant） 为零 <=> 不可逆
行列式（determinant） 不为零 <=> 可逆

![Alt text](/media/1d58611c1cbc186c0339.png)

![Alt text](/media/17a8416c3c55695b57f6.png)

![Alt text](/media/39942bfe29b6a17a0c68.png)

![Alt text](/media/592e3d001e06dd682ca7.png)

#### 正交矩阵（orthogonal matrix）

![Alt text](/media/c8e56f16fe1772a77969.png)

![Alt text](/media/dcb772b8cb7fbe9f3c8c.png)

![Alt text](/media/4b5daaaf10d6bad8b6f0.png)

  * c1, c2, c3 是单位向量

  * c1, c2, c3 互相垂直

标准正交基向量构成的矩阵

### 矩阵运算

  * 行（ row） 列（ column）

> 矢量
>  n × 1 的列矩阵（column matrix）
>  1 × n 的行矩阵（row matrix）

  * 不满足交换律

  * 满足结合律

![Alt text](/media/c44ecde5cb6989cb0a17.png)

矩阵左乘列向量

![Alt text](/media/f55e0a3d9f8fc42cc27d.png)

矩阵右乘行向量

![Alt text](/media/7b719438c4d8b1b57ce5.png)

以上两种方式等价。

```cpp
string s = String.Empty;

```

通常在变换定点时，使用右乘的方式，向量按照列矩阵来进行乘法。因为Unity提供的内置矩阵(如UNITY_MATRIX_MVP)都是按列存储的。

### 矩阵存储

Unity使用Cg语言的规定，内置矩阵按照列存储（向量是列向量），而填充方式（初始化方式）采用行优先。

C#层的Matrix4x4类型采用列优先方式填充。

### 矩阵的几何意义 - 变换

> 变换（transform）：把一些数据，如点、向量甚至颜色等，通过某种方式进行转换的过程。

#### 线性变换（linear transform）

  * 可以保留矢量加和标量乘的变换

![Alt text](/media/2f94c8a9d6a73c851332.png)

  * 保持网格平行且等距分布的变换

> 3x3矩阵可以表示所有的对三维矢量的线性变换

  1. 缩放 - scale

  2. 错切 - shear

  3. 镜像 - mirroring / reflection

  4. 正交投影 - orthographic projection

#### 仿射变换（affine transform）

> **平移变换** \- 不是线性变换，不满足标量乘法与矢量加法

**仿射变换** \- 合并线性变换和平移变换的变换类型
可以用4x4的矩阵表示。

需要把矢量扩展到四维空间下 - **齐次坐标空间（homogeneous coordinate space）**

> 转齐次坐标：
>  点 - 把w分量设置为1
>  向量 - 把w分量设置为0

仿射变换矩阵:
\- 表示旋转和缩放
\- 表示平移

##### 平移

平移点

![Alt text](/media/7c252ae9c7dabae2fd9a.png)

平移向量

![Alt text](/media/4a86f22a7052bb452249.png)

逆矩阵

![Alt text](/media/08f72c0d36c19ca14640.png)

##### 缩放

![Alt text](/media/17a8416c3c55695b57f6.png)

![Alt text](/media/39942bfe29b6a17a0c68.png)

统一缩放（uniform scale） -
不会改变角度和比例信息

非统一缩放（nonuniform scale） - 拉伸或挤压，改变模型相关角度和比例，影响到法线

逆矩阵

![Alt text](/media/592e3d001e06dd682ca7.png)

##### 旋转

绕x

![Alt text](/media/c8e56f16fe1772a77969.png)

绕y

![Alt text](/media/dcb772b8cb7fbe9f3c8c.png)

绕z

![Alt text](/media/4b5daaaf10d6bad8b6f0.png)

旋转顺序：
当给出
这样的旋转角度时

  * Unity旋转顺序为 - 绕固定坐标系，z->x->y

  * 绕坐标系E下的z轴旋转θz, 在坐标系E下绕z轴旋转θz后的新坐标系E’下的y轴旋转θy，在坐标系E’下绕y轴旋转θy后的新坐标系E”下的x轴旋转θx，即在旋转时，把 坐标系一起转动。

以上两种方式旋转结果相同。

![Alt text](/media/c44ecde5cb6989cb0a17.png)

##### 复合

**矩阵乘法不满足交换律，变换结果依赖变换顺序**
一般情况：缩放->旋转->平移

## 矩阵与坐标空间变换

### 坐标空间变换基础

定义一个坐标空间，必须指名其原点位置和3个坐标轴的方向。数值上相对于另一个坐标空间。
每个坐标空间都是另一个坐标空间的子空间，每个空间都有一个父坐标空间。对坐标空间的变换实质上就是在父空间和子空间之间对点和矢量进行变换。

![Alt text](/media/f55e0a3d9f8fc42cc27d.png)

已知点在子坐标空间c中点
，求A在父空间p中的坐标：
c的3个坐标轴在p空间中的坐标表示为
![Alt text](/media/7b719438c4d8b1b57ce5.png)

![Alt text](/media/a51acfc8f50d3701a0f9.png)

![Alt text](/media/1547a7fd486b9a6ee06b.png)

平移变换对矢量没有影响，因此在Shader中可以通过变换矩阵的前3行和前3列来对法线方向和光照方向等进行空间变换。

如果矩阵是正交矩阵，则求逆操作就是求转置操作：

![Alt text](/media/81e28b56406ed2340819.png)

如果知道子坐标空间的x、y、z轴在父空间下的坐标表示（必须是正交的单位矢量），就可以把他们依次放在矩阵的每一行，就可以得到从父坐标空间到子空间的变换矩阵了。

### 渲染管线中的坐标空间转换过程

![Alt text](/media/8cd5364c8a673ec5ed76.png)

#### 模型空间（model space） | 对象空间（object space） | 局部空间（local space）

自然方向：前(forward), 后(back), 左(left), 右(right), 上(up), 下(down)
Unity模型空间使用左手坐标系：

![Alt text](/media/836f503d028d2850cca7.png)

#### 世界空间(world space)

我们所关心的最外层的空间
Unity中，Transform.Position指的是相对于这个transform的父节点(parent)的模型坐标空间中原点的位置。如果一个Transform没有父节点，这个位置就是世界坐标系中的位置。

**模型变换（Model transform）：模型空间- >世界空间**

#### 观察空间(view space) | 摄像机空间(camera space)

右手坐标系，+z轴指向摄像机后方，符合OpenGL传统。
观察变换（view transform）：世界空间-> 观察空间

#### （齐次）裁剪空间（clip space）

**裁剪矩阵（clip matrix） | 投影矩阵（projection matrix）** 由视锥体（view frustum）决定
投影矩阵的目的：

  1. 为投影做准备。经过投影矩阵变换后，顶点的w分量将会具有特殊的意义。

投影矩阵并不进行投影。真正地投影过程会在 **屏幕映射** 时发生，通过 **齐次除法（homogeneous division）** 得到二维坐标。

  2. 对x、y、z分量进行缩放。

##### 透视投影（perspective projection）

![Alt text](/media/52b1b8b3dc04df125056.png)

本质是对x、y和z分量进行了不同程度的缩放，z分量还做了一个平移。

![Alt text](/media/3a1a2f6bda8f737460f7.png)

该矩阵会视锥体变换为中心在相机空间原点、边长为2的正方体。
w分量为z的绝对值，表示到相机的距离，表示缩放值。
转换为三维空间坐标(x/w, y/w, z/w)。
顶点在视锥体内，要求
即，若一个顶点在视锥体内，必须满足
如何理解四维坐标，如何理解w分量？
本质问题是，将一个frustum(截头锥体)压缩成一个正方体，求frustum中的点的坐标到正方体空间中点的坐标的一个映射关系。并用矩阵、线性代数的语言描述这一变换，使得所有的点的变换可以用同一个矩阵乘法进行描述，从而可以让计算机对所有顶点批量处理这一变换。

关键点：这个变换不是线性变换，无法用三维矩阵乘法描述。这个变换对所有点不是统一的，还与点到摄像机的距离有关系，不能对所有的点进行统一的变换，意味着对每个点增加一个属性，来描述点到摄像机的距离属性，从而使得这个变换与距离有关，这个属性就是点的w分量。

引入w分量之后，点的坐标扩充到四维，变换矩阵是4x4的矩阵。使用四维矩阵进行投影变换本质上是使用四维空间下的线性变换描述三维空间下的非线性变换（将frustum“捏”成正方体的变换）。
引入w分量后，实际上将一个点映射成了无数个点。我们定义了三维空间中点(x, y, z)对应的齐次坐标为(wx, wy, wz, w),(w!=0)。

之前“捏”frustum的操作，用三维空间线性变换是无法描述的，用3x3矩阵乘法进行变换是无法达到要求的。我们发现，当扩充一个维度之后，看待这个问题，之前的一些”无理要求”是可以满足的。

按照把frustum“捏”成正方体的条件，建立方程，在齐次空间下求这个映射函数，求出了满足要求的变换矩阵。变换之后，点的坐标是下图右边这个样子：

![Alt text](/media/3cc708bcd3c8f07b4e2f.png)

再映射到三维空间的点就是这个样子：

![Alt text](/media/d7d7722731978af0a43b.png)

这样一来，我们对所有的点都可以进行这个变换过程:

  1. 映射到齐次坐标空间（增加一个w分量，变为四维坐标）

  2. 按照我们对视锥体进行形变的要求，求得4x4变换矩阵。

  3. 齐次空间下做统一的缩放、平移变换，这个变换过程中，点将到摄像机的距离体现在w分量上面。

  4. 变换后，再将点映射到三维空间，发现原视锥体经变换后成功映射为正方体，其他所有点也经过了相同的映射，结果是合理的。

##### 正交投影（orthographic projection）

保留了物体的距离和角度，用于2D游戏或渲染小地图等其他HUD元素

> HUD（Head-Up Display）来源于航空，在飞行器中，HUD 指投射到挡风玻璃上的读数，让飞行员可以不用低头就可以看到这些信息。
>
![Alt text](/media/7bc1a47e16f168a221c9.png)

#### 屏幕空间（screen space）

##### 齐次除法（homogeneous division） | 透视除法（perspective division）

用齐次坐标的w分量去除x、y、z分量，得到 **归一化的设备坐标（Normalized Device Coordinates, NDC）** 。
经过透视投影变换后的裁剪空间，经过齐次除法后会变换到一个立方体内。

> OpenGL传统（Unity），z分量的范围是[-1, 1]。DirectX中，z的范围是[0, 1]。

#### 屏幕空间（screen space）

##### 屏幕映射

**屏幕坐标：** Unity中，屏幕左下角像素坐标是(0, 0)，右上角像素坐标是(pixelWidth, pixelHeight)。
z分量用于深度缓冲。
Unity中，裁剪空间->屏幕空间的过程是由底层帮我们完成的。

**视口空间（viewport space）坐标：** 将屏幕空间坐标归一化，屏幕坐标除以分辨率，得到视口空间坐标。屏幕左下角是(0, 0)，右上角是(1, 1)。

### 法线变换

  * 法线（normal）| 法矢量（normal vector）

  * 切线（tangent）|切矢量（tangent vector） - 通常与纹理空间对齐，与法线方向垂直

原坐标空间下：
得到
* 变换只包括旋转变换 => 变换矩阵是正交矩阵 =>
=> 直接用顶点变换矩阵来变换法线

  * 包含旋转和统一缩放 =>
* 包含非统一缩放 => 必须求解逆矩阵

## 其他问题

  1. 矩阵乘法中，参数的位置决定了对向量按照列矩阵还是行矩阵进行 乘法

  2. Cg中float4x4矩阵是按行有限的方式进行填充的；Unity Matrix4x4矩阵采用列优先的方式存储。

  3. 不能在顶点着色器当中进行齐次除法。顶点着色器中应当保留x、y、w分量。因为从顶点着色器到片元着色器过程中还有一个插值的过程，而插值往往是线性的。如果进行了齐次除法，得到的坐标空间不是线性的，不可以在投影空间中进行插值。片元函数中插值已经结束，得到归一化设备坐标的步骤应当在插值之后进行。

## Ref

《Unity Shader入门精要》 - 冯乐乐
