第 22 章 四元数
在3D图形编程中,旋转表示是一个核心问题。虽然旋转矩阵和欧拉角是常见的旋转表示方法,但四元数(Quaternion)因其在数学特性和计算性能上的优势,已成为现代3D应用中表示和处理旋转的首选方法。本章将详细介绍四元数的理论基础和在DirectX 12中的应用实践。
22.1 复数回顾
在深入四元数之前,我们需要先回顾复数的概念,因为四元数是复数的自然扩展。
22.1.1 定义
复数是形如 a + bi 的数,其中 a 和 b 是实数,i 是虚数单位,满足 i² = -1。复数可以表示为:
z = a + bi
其中:
- a 称为实部,记作 Re(z)
- b 称为虚部,记作 Im(z)
- i 是虚数单位
22.1.2 复数的几何意义
复数可以在二维平面中表示,即复平面。在复平面中:
- 实部对应x轴
- 虚部对应y轴
- 复数 a + bi 对应平面上的点 (a, b)
从几何角度看,复数可以表示二维平面中的旋转和缩放:
- 复数的模长 |z| = √(a² + b²) 表示缩放因子
- 复数的辐角 θ = atan2(b, a) 表示旋转角度
当一个复数乘以另一个复数时,它们的模长相乘,辐角相加,这正是旋转和缩放的组合。
22.1.3 极坐标表示法与旋转操作
复数可以使用极坐标形式表示:
z = r(cos θ + i sin θ) = re^(iθ)
其中:
- r 是复数的模长,r = |z| = √(a² + b²)
- θ 是复数的辐角,θ = atan2(b, a)
复数的乘法在极坐标形式下特别简单:
z₁ · z₂ = r₁e^(iθ₁) · r₂e^(iθ₂) = r₁r₂e^(i(θ₁+θ₂))
这表明复数乘法对应于旋转和缩放的复合变换:
- 模长相乘表示缩放
- 辐角相加表示旋转
单位复数(模长为1的复数)可以表示纯旋转,这是复数在2D旋转中的关键应用。例如,复数 cos θ + i sin θ 表示逆时针旋转 θ 角度。
22.2 四元数代数
四元数是复数的高维扩展,由爱尔兰数学家威廉·罗文·汉密尔顿(William Rowan Hamilton)于1843年发明。
22.2.1 定义与基本运算
四元数是形如 w + xi + yj + zk 的数,其中 w, x, y, z 是实数,i, j, k 是三个虚数单位,满足:
i² = j² = k² = ijk = -1
ij = k, ji = -k
jk = i, kj = -i
ki = j, ik = -j
一个四元数可以写为:
q = w + xi + yj + zk
或向量形式:
q = [w, v] = [w, (x, y, z)]
其中:
- w 称为四元数的标量部分(实部)
- v = (x, y, z) 称为四元数的向量部分(虚部)
四元数的基本运算包括:
-
加法:
q₁ + q₂ = (w₁ + w₂) + (x₁ + x₂)i + (y₁ + y₂)j + (z₁ + z₂)k
-
标量乘法:
复制
sq = sw + sxi + syj + szk
-
乘法:
q₁q₂ = [w₁, v₁][w₂, v₂] = [w₁w₂ - v₁·v₂, w₁v₂ + w₂v₁ + v₁×v₂]
展开形式为:
q₁q₂ = (w₁w₂ - x₁x₂ - y₁y₂ - z₁z₂) + (w₁x₂ + x₁w₂ + y₁z₂ - z₁y₂)i + (w₁y₂ + y₁w₂ + z₁x₂ - x₁z₂)j + (w₁z₂ + z₁w₂ + x₁y₂ - y₁x₂)k
22.2.2 特殊乘积
四元数乘法有一些特殊的情况值得注意:
-
纯四元数乘法:如果 q₁ = [0, v₁] 和 q₂ = [0, v₂] 是两个纯四元数(标量部分为0),则:
q₁q₂ = [-v₁·v₂, v₁×v₂]
-
与标量的乘法:如果 q₁ = [w₁, 0] 是一个标量四元数,q₂ = [w₂, v₂] 是任意四元数,则:
q₁q₂ = [w₁w₂, w₁v₂]
22.2.3 性质
四元数有以下重要性质:
-
非交换性:一般情况下,q₁q₂ ≠ q₂q₁
-
结合性:(q₁q₂)q₃ = q₁(q₂q₃)
-
单位四元数:1 = [1, (0, 0, 0)],对任何四元数 q,有 1q = q1 = q
-
共轭:四元数 q = [w, (x, y, z)] 的共轭是 q* = [w, (-x, -y, -z)]
-
模长:四元数的模长定义为 |q| = √(w² + x² + y² + z²)
-
单位四元数:如果 |q| = 1,则 q 是单位四元数,表示纯旋转
22.2.4 转换
四元数可以转换为其他旋转表示形式,如旋转矩阵和欧拉角:
四元数到旋转矩阵的转换:
给定单位四元数 q = [w, (x, y, z)],对应的旋转矩阵是:
apache
R = [1 - 2y² - 2z², 2xy - 2wz, 2xz + 2wy]
[ 2xy + 2wz, 1 - 2x² - 2z², 2yz - 2wx]
[ 2xz - 2wy, 2yz + 2wx