3D Mathematics 04 - Quaternion
# 概述
四元数 (Quaternion) 是3D引擎中另一种用于表示旋转的数学实体,在许多情况下,四元数要优于旋转矩阵,因为它使用更少的存储空间,在进行连接时需要更少的计算,并且更易于进行插值以产生平滑动画。
四元数的集合由 表示,它可以被认为是一个四维向量空间,数学家称之为 汉密尔顿四元数环 (the ring of Hamiltonian quaternions) 。
起初,四元数并不是专用于旋转的。作为用于描述现实空间的坐标表示方式,数学家Hamilton在复数的基础上创造了四元数,并以四元数的形式来表示空间中一点的坐标,那时向量和矩阵还未被推广使用。后来,四元数的地位已经几乎被向量和矩阵所取代。
但是在表示旋转时,相对于旋转矩阵和欧拉角而言,四元数具有独特的优势,因而现在四元数主要还是被用于表示旋转。
# 定义
我们定义一个四元数为:
其中 作为一种特殊的虚数单位参与运算,并遵循如下规则:
也可以用标量-向量表示法来描述,这种表示形式和复数非常相似:
其中 表示 的标量部分,即 ,称为四元数的 实部 (real component) ,而 表示 的向量部分,即 ,称为四元数的 虚部 (imaginary component) 。
类似于复数,我们定义四元数 的 共轭 (conjugate) 为:
当四元数的实部 时,称 为纯四元数,这有些类似于纯虚数的定义。它可以用于表示三维空间中的一点 。
# 基本性质
# 四元数的基本运算
设 是一个四元数, 是一个标量 (Scalar) ,定义四元数的标量乘法为:
设 是两个四元数,定义四元数的加法为:
定义四元数的减法为:
定义四元数的点积为:
定义四元数的叉积为:
设 是一个四元数,定义四元数的模为:
这些运算都和向量的运算基本一致,因而不再赘述。并且随着四元数不再被用于表示坐标,这些运算在3D引擎中事实上几乎用不到,只做了解即可。四元数最重要也是最复杂的运算是乘法运算。
# 四元数的乘法
虽然和复数很相似,但是四元数的乘法不满足交换律。
设 ,定义四元数的乘法如下:
也可以用标量-向量表示法来描述,设 ,则:
该式可以由上式根据点积和叉积的运算律替换得到。
四元数 与其共轭 的乘积等于 和自身的点积,也就等于其模的平方 。也就是说:
这可以帮助我们定义四元数的逆:
证明很简单:
另外,四元数的乘积的模满足如下规律:
将左右两边展开计算即可证明,此处略。
# 四元数和旋转
# 用四元数表示旋转
给定一个任意的向量 ,我们要将其绕单位向量 旋转 角度,这样的旋转可以由一个旋转矩阵来表示。现在我们考虑如何用四元数来表示。
用于表示三维空间中的旋转的四元数的一般形式为:
因为 是一个单位向量,所以 是一个单位四元数。定义对 应用四元数 所表示的旋转为 ,则:
注意,为了与 进行乘法运算,我们将 视为一个纯四元数。前文已经讲过,纯四元数亦可用来表达空间中一点的坐标,与向量的表示是等价的。
下面我们证明 仍然是一个纯四元数。同样是为了方便参与运算,这里我们将 也视为一个纯四元数。
根据向量的叉积的运算律 可得:
设 ,则:
根据我们在推导旋转矩阵时得到的结果:
对比可得:
求解可得:
值得一提的是,对四元数 乘上任意的非零标量,都不会改变旋转的结果:
此外,用四元数表示旋转仍然可以做到类似于旋转矩阵的级联运算:
因此这毫无疑问是一种非常优秀的表示方式。
# 正确性证明
三维空间中的旋转可以被看作是一个函数 ,该函数是一个从 到 的映射,且其变换结果必须保持长度不变、保持角度不变、保持手性不变。
对于三维空间中的一点 ,旋转保持长度 当且仅当:
对于三维空间中的两点 ,旋转保持角度 当且仅当:
对于三维空间中的两点 ,旋转保持手性当且仅当:
我们将函数 扩展为一个从 到 的映射,定义 ,那么我们可以将旋转保持角度的条件改写为:
由于现在值域为 ,自变量 可以被视为纯四元数。
根据四元数的乘法运算律,有 ,由此可以将旋转保持角度的条件和旋转保持手性的条件合并为一个表达式:
现在我们证明 满足条件。
对于保持长度:
对于保持角度和手性:
得证。
# 四元数与旋转矩阵
我们经常需要将四元数转换为等价的 的旋转矩阵,例如,为了参与变换矩阵的级联运算,或是将对象的变换传递给3D图形库。
将表达式
转换为矩阵表示形式:
设 ,由 可得:
由 是单位向量且 是单位四元数可得:
用 替换 可得:
求和并根据 可得等价的旋转矩阵 为:
# 球面线性插值
# 线性插值
因为四元数可以看作是由四维向量表示的,所以它们非常适合进行插值。当我们对一个对象进行动画处理时,插值对于生成落在预先计算的关键帧之间的中间状态很有用。
最简单的插值类型是线性插值。对于两个单位四元数 ,线性插值的四元数 由下式给出:
当 从 到 变化时,函数 沿着连接 和 的线段平滑地变化。然而 不保持 的单位长度,如图所示。
因此我们需要重新进行归一化,将公式改写为:
遗憾的是,尽管线性插值很高效,但是 随 的变化从 滑向 时,并非是以恒定的速率运动的。如图所示, 的值揭示了在 和 之间的角度变化的速率在 和 的端点附近相对较慢,而在 附近最快。
# 球面线性插值
我们需要找到一个插值函数 ,它必须保持单位长度,并且随 以匀速发生变化。也就是说,设 之间的夹角为 角度,当 从 到 变化时, 之间的夹角应当为 角度,如图所示。
我们先假设 为:
在等式两边同时点乘 ,因为 具有相同的长度,消去 可得:
类似地,在等式两边同时点乘 可得:
此时对于 已经有两个方程,解方程组可得:
化简可得:
现在我们可以给出四元数的 球面线性插值 (spherical linear interpolation) 的定义:
其中 。考虑到 和 表示的是相同的旋转,为方便起见,我们通常会取 ,这也确保了插值在最短路径上进行。
此外 ,如果有必要的话可以将公式中的 替换掉。
- 01
- Reading Papers - Kernel Concurrency06-01
- 02
- Linux Kernel - Source Code Overview05-01
- 03
- Linux Kernel - Per-CPU Storage05-01