# 变换


## ***引言***

*变换（transform）是指以点、向量、颜色等实体作为输入，并以某种方式对其进行转换的一种操作。对于计算机图形学从业者而言，熟练掌握变换相关的知识是非常重要的。通过各种变换操作，我们可以对物体、光源和相机进行移动、变形以及设定动 画；我们还可以确保所有的计算都在同一个坐标系下进行，以及使用不同的方式来将物体投影到一个平面上。这里我们只列举了变换所能完成的部分操作，但是足以证明变换在实时图形学中的重要性，或者可以说，在任何领域图形学中的重要性。*

1. ***线性变换***

   *线性变换是指一种仅保留向量加法个标量乘法的变换，具体来说就是：*
   $$
   f(x) + f(y) = f(x+y)
   \\\ \\\
   kf(x) = f(kx)
   $$

2. ***缩放变换***

   *例如：现在有一个变换 $f(x) = 5x$，他代表将输入向量 x 的每个分量都乘以 5。为了证明这个变换是线性的，他必须满足上述两个条件，目前确实也是满足的。*

   ***这种函数就叫做缩放变换**，因为他改变了一个物理的大小（尺寸）*

3. ***旋转变换***

   *旋转变换是另一种线性变换，他将一个向量以原点为中心进行旋转。*

4. ***平移变换***

   *函数 $f(x) = x + (7, 3, 2)$是一个非线性变换，其中的向量x是一个三维向量。对两个不同的向量分别执行这个函数，其结果为每个向量各自加上(7, 3, 2)，这就是平移变换。*

   *也就是说让一个向量加上另一个固定向量，意味着完成了一次平移变换*

5. ***仿射变换***

   *仿射变换通常存储在一个 4 x 4 的矩阵中*

   *仿射变换是指先进性一次线性变换，然后在进行一次平移操作的变换。*

   *我们使用其次符号来表示这样的四维向量。点和方向也会使用同样的方式来进行表示（小写的粗体字母）*

   *实时渲染中所使用的平移、旋转、缩放、对称和剪切矩阵都是仿射类型的。*

   *仿射变换最主要的特征就是它保证了直线的平行性（即两个平行的直线在变换之后仍然是平行的），但是其长度和角度可能会发生一些变化。一个仿射变换也可以表示为一系列独立仿射变换的组合。*

*缩放变换和旋转变换以及事实上所有应用三维向量的线性变换，都可以使用一个 3 x 3 矩阵来进行表示；但是，3 x 3的矩阵尺寸通常是不够的*

| *符号*                                                      | *名称*         |                          *特征描述*                          |
| :---------------------------------------------------------- | :------------- | :----------------------------------------------------------: |
| **$T(\mathbf{t})$**                                         | *平移矩阵*     |        *移动一个点；是仿射变换，保持方向和长度不变。*        |
| **$R_x(\rho)$**                                             | *旋转矩阵*     | *绕 x 轴旋转 $\rho$ 弧度；绕 y, z 轴是类似的；是正交矩阵 & 仿射变换。* |
| **$\mathbf{R}$**                                            | *旋转矩阵*     | *任意的旋转矩阵；是正交矩阵，保持长度和角度不变；是仿射变换。* |
| **$S(\mathbf{s})$**                                         | *缩放矩阵*     | *根据传入的向量 $\mathbf{s}$，沿 x, y, z 轴进行缩放；是仿射变换。如果 $\mathbf{s}$ 的分量不同，则为非均匀缩放。* |
| **$H_{ij}(s)$**                                             | *剪切变换*     | *将分量 i 相对于分量 j 进行剪切；例如，$H_{xy}(s)$ 将 x 坐标偏移 $s \cdot y$；是仿射变换。* |
| **$E(h, p, r)$**                                            | *欧拉变换*     | *根据给定的欧拉角 (yaw, pitch, roll) 创建一个表示朝向的旋转矩阵；是正交矩阵 & 仿射变换。* |
| **$\mathbf{P}_o(s)$**                                       | *正交投影矩阵* | *平行投影到一个平面或者是规范观察体积（view volume）；是线性变换，但会降低维度。* |
| **$\mathbf{P}_p(s)$**                                       | *透视投影矩阵* | *透视投影到一个平面或者视锥体（frustum）；这不是仿射变换，而是射影变换。* |
| ***$\text{slerp}(\hat{\mathbf{p}}, \hat{\mathbf{r}}, t)$*** | *球面插值*     | *根据参数 $t$，在两个单位四元数 $\hat{\mathbf{p}}$ 和 $\hat{\mathbf{r}}$ 之间进行球面线性插值，返回一个插值后的新四元数。* |

## ***基本变换***

*本小节会介绍最基本的变换操作，例如平移、旋转、缩放、剪切、变换组合、刚体变换、法线变换以及计算逆矩阵等。*

### ***平移***

#### ***平移矩阵***

$$
T(t) = T(t_x, t_y, t_z) = 
\begin{bmatrix}
1 & 0 & 0 & t_x
\\\ \\\
0 & 1 & 0 & t_y
\\\ \\\
0 & 0 & 1 & t_z
\\\ \\\
0 & 0 & 0 & 1
\end{bmatrix}
$$

*图4.1 展示了平移变换的一个具体效果*

* *很容易看出，点 $p = (p_x, p_y, p_z, 1)$ 在经过$T(x)$变换之后，生成了一个新的顶点$p' = (p_x + t_x, p_y+t_y, p_z + t_z, 1)$，很显然这是一个平移变换*
* *需要注意的是，一个向量$v = (v_x, v_y, v_z, 0)$和矩阵$T$ 相乘后，是不会收到影响的，因为一个方向向量是无法被平移的*
* *平移矩阵的逆矩阵就是反向平移相同距离的矩阵*

![平移矩阵](https://raw.githubusercontent.com/vlicecream/cloudImage/main/image-20251112100255069.png)

#### ***DirectX矩阵的表示***

*DirectX 会把平移向量放在变换矩阵的最下面一行。在这种表示方式中，矩阵元素的顺序被颠倒，即应用程序会以从左往右的方式来读取平移变量；*
$$
T(t) = 
\begin{bmatrix}
1 & 0 & 0 & 0
\\\ \\\
0 & 1 & 0 & 0
\\\ \\\
0 & 0 & 1 & 0
\\\ \\\
t_x & t_y & t_z & 1
\end{bmatrix}
$$


*这种向量和矩阵的表示方法被称作行优先表示法；我们上述例子叫做列优先表示法。使用哪一种表示方法仅仅是符号上的区别。*

*当我们采用行主序表示法，那么变换矩阵在内存中进行存储时，代表位移的四个分量会位于所有 16 个分量的最后四位，其中前三位代表了具体的位移分量，最后 一位是 1。*

### ***旋转***

*旋转变换可以将一个向量（位置或者方向），绕着一个过原点的旋转轴旋转一定的角度。*

*与平移变换一样，旋转变换也是一个刚体变换，也就是说，被变换点之间的距离并不会发生改变，并且保持了手性，即他不会导致物体左右两边相互变换*

*这两个特性对于计算机图形学中调整物体位置和朝向而言非常有用*

#### ***方向矩阵***

*方向矩阵是一个与相机视角和物体朝向相关的旋转矩阵，它定义了物体在空间中的朝向，即物体向上和向前的方向*

#### ***x轴旋转矩阵***

$$
R_x(\phi) =
\begin{bmatrix}
1 & 0 & 0 & 0
\\\ \\\
0 & \cos\phi & -\sin\phi & 0
\\\ \\\
0 & \sin\phi & \cos\phi & 0
\\\ \\\
0 & 0 & 0 & 1
\end{bmatrix}
$$

#### ***y轴旋转矩阵***

$$
R_y(\phi) =
\begin{bmatrix}
\cos\phi & 0 & \sin\phi & 0
\\\ \\\
0 & 1 & 0 & 0
\\\ \\\
-\sin\phi & 0 & \cos\phi & 0
\\\ \\\
0 & 0 & 0 & 1
\end{bmatrix}
$$

#### ***z轴旋转矩阵***

$$
R_z(\phi) = 
\begin{bmatrix}
\cos\phi & -\sin\phi & 0 & 0
\\\ \\\
\sin\phi & \cos\phi & 0 & 0
\\\ \\\
0 & 0 & 1 & 0
\\\ \\\
0 & 0 & 0 & 1
\end{bmatrix}
$$

### ***缩放***

#### ***缩放矩阵***

$$
S(s) = 
\begin{bmatrix}
s_x & 0 & 0 & 0
\\\ \\\
0 & s_y & 0 & 0
\\\ \\\
0 & 0 & s_z & 0
\\\ \\\
0 & 0 & 0 & 1
\end{bmatrix}
$$

#### ***均匀缩放 / 非均匀缩放***

*当 $s_x = s_y = s_z$的时候，这个缩放操作被称为均匀缩放，否则会被称作非均匀缩放*

### ***剪切***

*剪切变换是另一种基本变换，它可以用来对整个场景进行扭曲，从而创造一种迷幻的效果，或者是对单个模型的外观进行扭曲。*

*剪切变换一共包含 6 个基本矩阵*

* $$
  H_{xy}(s) = \begin{bmatrix}
  1 & s & 0 & 0
  \\\ \\\
  0 & 1 & 0 & 0
  \\\ \\\
  0 & 0 & 1 & 0
  \\\ \\\
  0 & 0 & 0 & 1
  \end{bmatrix}
  \quad \quad \quad \quad \quad \quad \quad \quad \quad \quad
  H_{xz}(s) = 
  \begin{bmatrix}
  1 & 0 & s & 0
  \\\ \\\
  0 & 1 & 0 & 0
  \\\ \\\
  0 & 0 & 1 & 0
  \\\ \\\
  0 & 0 & 0 & 1
  \end{bmatrix}
  $$

* $$
  H_{yx}(s) = \begin{bmatrix}
  1 & 0 & 0 & 0
  \\\ \\\
  s & 1 & 0 & 0
  \\\ \\\
  0 & 0 & 1 & 0
  \\\ \\\
  0 & 0 & 0 & 1
  \end{bmatrix}
  \quad \quad \quad \quad \quad \quad \quad \quad \quad \quad
  H_{yz}(s) = \begin{bmatrix}
  1 & 0 & 0 & 0
  \\\ \\\
  0 & 1 & s & 0
  \\\ \\\
  0 & 0 & 1 & 0
  \\\ \\\
  0 & 0 & 0 & 1
  \end{bmatrix}
  $$

* $$
  H_{zx}(s) = \begin{bmatrix}
  1 & 0 & 0 & 0
  \\\ \\\
  0 & 1 & 0 & 0
  \\\ \\\
  s & 0 & 1 & 0
  \\\ \\\
  0 & 0 & 0 & 1
  \end{bmatrix}
  \quad \quad \quad \quad \quad \quad \quad \quad \quad \quad
  H_{zy}(s) = \begin{bmatrix}
  1 & 0 & 0 & 0
  \\\ \\\
  0 & 1 & 0 & 0
  \\\ \\\
  0 & s & 1 & 0
  \\\ \\\
  0 & 0 & 0 & 1
  \end{bmatrix}
  $$

*其中第一个下标用于表示哪个坐标会被剪切矩阵改变，第二个下标表示将会使用哪个坐标来进行剪切。*

*我们从 $H_{xz}(s)$ 中发现，剪切矩阵的两个下标可以用来找到参数 s 在矩阵中的位置：下标中的 x（其索引为 0）代表了第0行，下标中的 z （其索引为2）代表了第2列*

*将这个矩阵和一个顶点相乘，会生成一个新的顶点：$(p_x + sp_z \quad p_y \quad p_z)^T$*

*下附图生动的展示了一个单位正方形被剪切的过程。通过向相反的方向进行剪切，我们可以获得 $H_{ij}(s)$的逆矩阵，即$ H_{ij}^{-1}(s) = H_{ij}(-s) $*

![剪切](https://raw.githubusercontent.com/vlicecream/cloudImage/main/%E5%89%AA%E5%88%87%E7%9F%A9%E9%98%B5.png)

*你还可以使用一种稍微不同的剪切矩阵：*
$$
H'\_{xy}(s,t) = 
\begin{bmatrix}
1 & 0 & s & 0
\\\ \\\
0 & 1 & t & 0
\\\ \\\
0 & 0 & 1 & 0
\\\ \\\
0 & 0 & 0 & 1
\end{bmatrix}
$$
*这里的剪切变换包含两个输入参数，他们代表了这两个坐标（x, y）都会被第三个坐标（z）剪切。这个特殊的剪切矩阵可以通过上面一般的剪切矩阵组合而成，即 $H'\_{xy}(s,t) = H_{ik}(s)H_{jk}(t)$，其中的 k 代表了第三个坐标分量的索引。*

*最后我们需要注意的是，任何剪切矩阵的行列式值都为 $|H| = 1$，这意味着剪切变换是一种体积保持的变换，即变换前后，物体的体积并不会发生改变*

### ***变换的连接***

*由于矩阵之间的乘法不具备交换律（noncommutativity），因此矩阵在乘法式子中的顺序十分重要。变换的连接是与顺序相关的。*

*举例：我们现在有两个矩阵 $S,R$*

* *其中矩阵 $S(2, 0.5, 1)$是一个缩放变换，他将坐标的 x分量缩放为原来的的2倍，将 y 分量缩放为原来的 0.5 倍。*
* *$R_z(\pi / 6)$是一个旋转矩阵，他绕 z 轴（在右手坐标系中，这里的 z 轴指向书页的外面）顺时针旋转了 $\pi / 6$的角度*

*这两个矩阵可以用两种不同的方式相乘，他们变换的结果是完全不同的。*

![变换的连接](https://raw.githubusercontent.com/vlicecream/cloudImage/main/%E5%8F%98%E6%8D%A2%E7%9A%84%E8%BF%9E%E6%8E%A5.png)

*将一系列矩阵组合成一个独立矩阵有一个好处，那就是可以获得更高的执行效率。例如：想象我们现在有一个包含几百万顶点的游戏场景，场景中的所有物体都需要进行缩放、旋转和平移变换。这里我们并不会将所有的顶点都与这三个变换矩阵挨个相乘，因为这样做的效率实在是太低了；我们会将这三个矩阵连接成一个独立的矩阵， 然后对所有顶点都应用这个相同的组合变换矩阵。*

*这个组合矩阵可以写作 $C = TRS$，请注意这里的矩阵顺序，缩放矩阵S应当首先作用于顶点，因此他出现在组合矩阵的最右侧。*

*这个组合矩阵的顺序意味这 $TRSp = (T(R(Sp)))$，其中p是待变换的顶点。*

*矩阵的交换律不行，但是矩阵的结合律是可以的。即$(TR)(Sp)$是可以的*

### ***刚体变换***

*只包含平移和旋转的变换叫做刚体变换（rigid-body transform）*

*任何刚体变换矩阵X， 都可以写成一个平移矩阵 $T(t)$ 和一个旋转矩阵 $R$ 的连接*
$$
X = T(t)R = \begin{bmatrix}
r_{00} & r_{01} & r_{02} & t_x
\\\ \\\
r_{10} & r_{11} & r_{12} & t_y
\\\ \\\
r_{20} & r_{21} & r_{22} & t_z
\\\ \\\
0 & 0 & 0 & 1
\end{bmatrix}
$$
*刚体变换矩阵 $X$ 的逆矩阵可以通过这两种方式来计算：*

1. $$
   X^{-1} = (T(t)R)^{-1} = R^{-1}T(t^{-1}) = R^TT(-t)
   $$

2. $$
   \left.\mathbf{\bar{R}}=\left(
   \begin{array}
   {ccc}\mathbf{r}\_{,0} & \mathbf{r}\_{,1} & \mathbf{r}\_{,2}
   \end{array}\right.\right)=
   \begin{pmatrix}
   \mathbf{r}_0^T, \\\ \\\
   \mathbf{r}_1^T, \\\
   \mathbf{r}_2^T,
   \end{pmatrix}, \\\
   \mathbf{X}=
   \begin{pmatrix}
   \mathbf{\bar{R}} & \mathbf{t} \\\
   \mathbf{0}^T & 1
   \end{pmatrix},
   $$

   *其中 $r_{,0}$ 代表了旋转矩阵中的第一列（即第一个逗号取值可以是 0-2，而第二个下标的值始终为 0）*

   *$r_0^t$ 代表了旋转矩阵中的第一行。*

   *方程中的 0 代表一个 3 x 1 的零向量。*

### ***法线变换***

*矩阵可以用于对点、线、三角形和其他几何物体进行变换，这些矩阵同样也可以对这些线或者三角形表面的切向量进行变换，然而有一个重要的几何属性并不能总是使用这些矩阵直接进行变换，即表面法线（以及顶点的光照法线）。*

*下图展示了如果使用同样的矩阵同时对几何物体及其发现进行变换的结果*

![法线变换](https://raw.githubusercontent.com/vlicecream/cloudImage/main/%E6%B3%95%E7%BA%BF%E5%8F%98%E6%8D%A2.png)

#### ***法线变换的正确方式***

*对法线正确的变换方式是：*

1. *使用原始变换矩阵的伴随矩阵的转置矩阵来对其进行变换，而不是使用原始变换矩阵本身。矩阵的伴随矩阵是始终存在的。*
2. *在变换后通常还需要对法线进行归一化处理。因为法线在经过变换之后，其长度可能会发生变化*

*实际上，我们甚至都不需要计算这个伴随矩阵。*

* *在这样的条件下（进行了平移、旋转和均匀缩放操作）， 我们可以直接使用模型的变换矩阵来对法线进行变换；*
* *但是如果模型变换涉及了非均 匀缩放或者投影操作，那么就无法使用这种方法对法线进行变换了。*

#### ***法线变换的传统方式***

1. *计算原始变换矩阵的逆矩阵的转置，即 $(M^{-1})^T$*
2. *缺点：*
   * *这个方法现在也是可以使用的。但是我们并不需要完整的求出这个逆矩阵，这样会很浪费性能*
   * *并且有时候这个逆矩阵可能并不存在，因为逆矩阵是通过伴随矩阵除以矩阵的行列式获得的，但是行列式可能会为0。行列式为 0 的矩阵被成为奇异矩阵*

#### ***最后是否归一化法线***

1. *如果模型变换只涉及平移或者旋转 的话，那么法线的长度是不会发生改变的，因此也就不需要进行归一化处理。*

2. *变换中涉及了非均匀缩放的话，那么这个均匀缩放系数（如果是已知的，或者是已经被提取出来的话），也可以直接用来对法线进行归一化处理（直接按照均匀缩放系数进行反向缩放即可）*

   *我们还可以将这一步放在对法线变换之前，让左上角 3 x 3 子矩阵除以这个缩放系数, 从而构建一个会生成归一化结果的法线变换矩阵*

3. *如果表面法线是从变换之后的三角形中计算出来的话（例如使用三角形的边向量进行叉乘，从而获得垂直于三角形表面的法线），那么法线变换的问题就不需要进行考虑了。切向量的本质和法线并不相同，它可以直接使用原始变换矩阵进行变换。*

### ***计算逆矩阵***

*有很多计算都需要使用逆矩阵，例如在不同的坐标系间来回切换的时候。根据一个变换的可用信息不同，我们可以使用以下三种方法来计算一个逆矩阵：*

* *如果某个矩阵是一次很简单的变换，或者是一系列带参简单变换的组合的话，那么我们可以通过反转“参数”和矩阵次序的方式，来获得这个矩阵的逆矩阵，即进行一系列的反向变换。*

  *例如：现在有一个变换矩阵 $M = T(t)R(\phi)$，则其逆矩阵为 $M^{-1} = R(-\phi)T(-t)$。这种求取逆矩阵的方式十分简单且准确，这对于渲染一个大世界而言是十分重要的*

* *如果一个矩阵是正交矩阵的话，那么其逆矩阵和转置矩阵是相等的，即$M^{-1} = M^T$。旋转矩阵是一个正交矩阵，并且任意数量的旋转矩阵组合在一起仍然是一个旋转矩阵，因此其结果也是正交的*

* *如果这些信息都不知道的话，那么我们还可以使用伴随矩阵法、Cramer法则、LU分解法、高斯消元法等方法来计算一个矩阵的逆矩阵。伴随矩阵法和Cramer法则通常要更好一些，因为他们所设计的分支操作较少；*

*在进行性能优化的时候，我们还可以对逆矩阵的计算目的进行考虑。例如：如果这个 逆矩阵仅仅是用来对向量进行变换的话，那么通常我们只需要获得左上角子矩阵的逆矩阵即可*

## ***特殊的矩阵变换和操作***

### ***欧拉变换***

*欧拉变换可以构建一个旋转矩阵，将我们自身（相机）或者其他物体指向一个特定的方向*

*首先，我们需要有一个默认的观察方向，通常来说都会让这个方向指向 z 轴负半轴，并且头部指向 y 轴正半轴。*

*欧拉变换是三个旋转矩阵相乘的结果，如下图。*

![欧拉变换](https://raw.githubusercontent.com/vlicecream/cloudImage/main/%E6%AC%A7%E6%8B%89%E5%8F%98%E6%8D%A2.png)

*欧拉变换通常会使用 $E$ 来进行表示：*
$$
E(h,p,r) = R_z(r)R_x(p)R_y(h)
$$
*由于矩阵 $E$ 是由一系列旋转矩阵连接组成的，那么矩阵 $E$ 本身自然也是一个正交矩阵，因此该矩阵的逆矩阵可以表示为 $E^{-1} = E^T = (R_z^T R_x^T R_y^t) = R_y^t R_x^T R_z^T$，当然，一般直接使用 $E^T$ 是最方便的*

*其中欧拉角参数 h, p, r 代表了每个方向（h 头部，pitch 俯仰角，roll 翻滚角），在有些地方 比如飞行模拟，Unreal中 head被叫做偏航角（yaw）*

* *改变 head（yaw）偏航角会让观察者摇头*
* *改变 pitch 俯仰角会让观察者点头*
* *改变 roll 翻滚角会让观察者歪头*

*欧拉变换不仅仅可以用来调整相机的方向，还可以用来调整任意物体的朝向；同时，欧拉变换不仅可以用于世界空间中，同样也可以用于局部参考坐标系中。*

*在一些欧拉角的表示中，会让 z 轴指向上方；这虽然会让人感到一些困惑，但是它确实只是一种符号表示上的差异，本质上都是等价的。计算机图形学中，在如何看待和表示世界这个问题上，确实存在着一些分歧，即： y 轴向上（y-up）还是 z 轴向上（z-up）。*

#### ***优点***

1. *欧拉角在小角度变换和调整观察者方向方面十分有用*

#### ***缺点***

1. *我们很难将两组欧拉角组合在一起*

   *在两组欧拉角之间进行插值，并不是简单地对每个分量分别进行插值就可以完成的。事实上，两组表示形式完全不同的欧拉角，可能会给出完全相同的方向，因此对这两组欧拉角进行插值的话，中间生成的任何一组欧拉角，在理想情况下都不应当导致物体发生旋转。这也是使用其他方向表示方法（例如四元数）的原因之一*

2. *万向节死锁*

### ***从欧拉变换中提取参数***

*在某些情况下，我们需要从一个代表欧拉变换的矩阵中，提取出各个方向上所改变的角度，即欧拉变换的参数 h, p ,r。这个过程如下所示：*
$$
E(h, p ,r) = \begin{bmatrix}
e_{00} & e_{01} & e_{02}
\\\ \\\
e_{10} & e_{11} & e_{12}
\\\ \\\
e_{20} & e_{21} & e_{22}
\end{bmatrix}
= R_z(r) R_x(p) r_y(h)
$$
*齐次变换矩阵是 4 x 4 的。这里我们只使用了左上角 3 x 3 子矩阵，因为这已经能够提供旋转矩阵的所有必要信息了；也就是说，在完整的 4 x 4 欧拉变换矩阵中，除了最右下角的元素是1之外，其他剩余的元素均为 0*

*我们将三个旋转矩阵相乘，可以获得以下结果：*
$$
\mathbf{E} = 
\begin{bmatrix}
\cos r \cos h - \sin r \sin p \sin h & -\sin r \cos p & \cos r \sin h + \sin r \sin p \cos h \\\ \\\
\sin r \cos h + \cos r \sin p \sin h & \cos r \cos p & \sin r \sin h - \cos r \sin p \cos h \\\ \\\
-\cos p \sin h & \sin p & \cos p \cos h
\end{bmatrix}
$$
*我们可以很明显的看出 $\sin p = e_{21}$; 此外，我们可以令 $e_{01}$ 除以 $e_{11}$ 来计算 r，令 $e_{20}$除以$e_{22}$来计算 h：*
$$
\frac{e_{01}}{e_{11}} = \frac{-\sin r}{\cos r} = -\tan r
\quad \quad \quad \quad \quad \quad \quad
\frac{e_{20}}{e_{22}} = \frac{-\sin h}{\cos h} = -\tan h
$$
*也就是说，我们可以使用 $atan2(y, x)$（包含两个参数的反正切函数）来从矩阵$E$中提取欧拉角的参数h, p, r。即：*
$$
h = atan2(-e_{20}, e_{22})
\\\ \\\
p = arcsin(e_{21})
\\\ \\\
r = atan2(-e_{01}, e_{11})
$$


*当我们使用欧拉变换的时候，有时会发生一种叫做万向节死锁的现象。*

*即在旋转的过程中失去了一个自由度。例如：假设我们现在按照的顺序 x, y, z 进行变换，然后绕 y 轴旋转了 $\pi / 2$ 的角度，即执行了第二个旋转；这个旋转操作会使得局部坐标系中的 z 轴与原始的 x 轴重合，最终导致绕 z 轴旋转的操作是多余的。*

### ***矩阵分解***

#### ***用途***

1. *提取物体的缩放因子。*
2. *找到一个指定坐标系中所需要的变换（例如：某些系统和变换不允许使用任意的 4 x 4 矩阵）*
3. *确定一个物体是否只经历了刚体变换*
4. *在只有物体的变换矩阵可用的情况下，在动画的关键帧之间进行插值*
5. *移除一个旋转变换矩阵中的剪切变换*

*在前文中我们其实已经展示了两个矩阵分解的例子*

* *例如从一个刚体变换中提取出平移矩阵和旋转矩阵；*

* *从一个正交矩阵中提取出欧拉角*

### ***绕任意轴旋转***

*矩阵如下：*
$$
R_{\mathbf{u}}(\theta) = 
\begin{bmatrix}
\cos\theta + u_x^2(1-\cos\theta) & u_x u_y(1-\cos\theta) - u_z \sin\theta & u_x u_z(1-\cos\theta) + u_y \sin\theta & 0 \\\ \\\
u_y u_x(1-\cos\theta) + u_z \sin\theta & \cos\theta + u_y^2(1-\cos\theta) & u_y u_z(1-\cos\theta) - u_x \sin\theta & 0 \\\ \\\
u_z u_x(1-\cos\theta) - u_y \sin\theta & u_z u_y(1-\cos\theta) + u_x \sin\theta & \cos\theta + u_z^2(1-\cos\theta) & 0 \\\ \\\
0 & 0 & 0 & 1
\end{bmatrix}
$$

## ***四元数***

*四元数可以用于稳定且恒定速度的方向插值，这是欧拉角很难实现的。*

### ***四元数的定义***

$$
\hat{\mathbf{q}} = (\mathbf{q}_v, \mathbf{q}_w) = iq_x + jq_y + kq_z + q_w = \mathbf{q}_v + q_w
\\\ \\\
\mathbf{q}_v = iq_x + jq_y + kq_z = (q_x, q_y, q_z)
\\\ \\\
i^2 = j^2 = k^2 = -1, jk = -kj = i, ki = -ik = j, ok = -ji = k
$$

* *$q_w$ 是四元数 $\hat{q}$ 中的实数部分（实部）*
* *$q_v$ 是四元数 $\hat{q}$ 中的虚数部分（虚部）*
* *i, j, k 叫做虚数单位。*

*四元数的结构和复数类似，但是复数只有一个虚部，而四元数则包含三个虚部*

### ***数学运算***

#### ***乘法***

$$
\begin{align*}
\hat{q}\hat{r} &= (iq_x + jq_y + kq_z + q_w)(ir_x + jr_y + kr_z + r_w)
\\\ \\\
&= i(q_yr_z - q_zr_y + r_wq_x + q_wr_x)
\\\ \\\
& \quad + j(q_zr_x - q_xr_z + r_wq_y + q_wr_y)
\\\ \\\
& \quad + k(q_xr_y - q_yr_x + t_wq_z + q_wr_z)
\\\ \\\
& \quad + q_wr_w - q_xr_x - q_yr_y - q_zr_z
\\\ \\\
&= (\mathbf{q}_v \times \mathbf{r}_v + r_w\mathbf{q}_v + q_w\mathbf{r}_v, q_wr_w - \mathbf{q}_v \cdot \mathbf{r}_v)
\end{align*}
$$

#### ***加法***

$$
\hat{\mathbf{q}} + \hat{\mathbf{r}} = (\mathbf{q}_v, q_w) + (\mathbf{r}_v, r_w) = (\mathbf{q}_v + \mathbf{r}_v, q_w + r_w) 
$$

#### ***共轭***

$$
\hat{\mathbf{q}}^* = (\mathbf{q}_v, q_w)^* = (-\mathbf{q}_v, q_w)
$$

#### ***模长***

$$
n(\hat{\mathbf{q}}) 
= \sqrt{\hat{\mathbf{q}} \hat{\mathbf{q}}^*} 
= \sqrt{\hat{\mathbf{q}}^* \hat{\mathbf{q}}} 
= \sqrt{\mathbf{q}_v \cdot \mathbf{q}_v + q^2_w}
= \sqrt{q^2_x + q^2_y + q^2_z + q^2_w}
$$

#### ***虚数单位***

$$
\hat{i} = (0, 1)
$$

#### ***四元数的逆***

$$
\hat{q}^{-1} = \frac{1}{n(\hat{q})^2} \hat{q}^*
$$

#### ***共轭法则***

$$
\begin{align*}
(\hat{\mathbf{q}}^*)^* &= \hat{\mathbf{q}}
\\\ \\\
(\hat{\mathbf{q}} + \hat{\mathbf{r}})^* &= \hat{\mathbf{q}}^* + \hat{\mathbf{r}}^*
\\\ \\\
(\hat{\mathbf{q}} \hat{\mathbf{r}})^* &= \hat{\mathbf{r}}^* \hat{\mathbf{q}}^*
\end{align*}
$$

#### ***模长法则***

$$
\begin{align*}
n(\hat{\mathbf{q}}^*) &= n(\hat{\mathbf{q}})
\\\ \\\
n(\hat{\mathbf{q}} \hat{\mathbf{r}}) &= n(\hat{\mathbf{q}})n(\hat{\mathbf{r}})
\end{align*}
$$

#### ***乘法分配律***

$$
\hat{\mathbf{p}}(s\hat{q} + t\hat{\mathbf{r}}) = 
$$


