4.3 Quaternions
虽然四元数早在1843年就由William Rowan Hamilton先生发明出来用于表示复数的扩展,但是直到1985年才由Shoemake[1176]把四元数引入到了计算机图形学领域。四元数是构建具有强大功能的变换操作的一个有力工具,而且在某些方面,四元数是比欧拉角度和矩阵更好的计算方式,特别是在旋转操作和方位计算方面。给定一个 轴&角度 的表示形式,转变成一个四元数或从四元数转变成原来的形式是直截了当的,而使用欧拉角度则无法在任何方向进行转换。四元数可以用于稳定并不断的确定方位的插值运算,而这是使用欧拉角度无法完成的。
复数包含一个实部和一个虚部。每一个部分由两个实数表示,其中第二个实数通过与−1−−−√相乘。类似的,四元数具有四个部分。前三个部分的值与旋转轴密切相关,因为旋转的角度会影响所有部分的值(4.3.2节有更多相关的内容)。每一个四元数由4个实数表示,每个实数对应于一个不同的部分。由于四元数具有四个分量,我们选择使用向量的形式表示它们,但是与向量不同的是,在记号上加一个帽子:q^。首先我们会讨论一些关于四元数的数学背景知识,然后用于构建一些有趣的,实用的变换运算。
4.3.1 Mathematical Background
我们从描述一个四元数的定义开始。
Definition. 可以使用下面的方法定义四元数 q^,所有的定义都同等的。
q^ qv i2 =(qv,qw)=iqx+jqy+kqz+qw=qv+qw,=iqx+jqy+kqz=(qx,qy,qz),=j2=k2=−1,jk=−kj=i,ki=−ik=j,ij=−ji=k.(4.28)
其中变量 qw 称为四元数 q^ 的实部,虚部为 qv,并且 i,j,k 称为虚部的基本单位。
对于四元数的虚部 qv,我们可以使用向量的所有基本运算,比如向量加法,向量-标量乘法,点乘,叉乘等等。使用四元数的定义,我们推导出两个四元数 q^ 和 r^ 的乘法运算,如下所示。需要注意的是虚部的乘法不满足交换律。
Multiplication(乘法):q^r^ =(iqx+jqy+kqz+qw)(irx+jry+krz+rw)==i(qyrz−qzry+rwqx+qwrx) +j(qzrx−qxrz+rwqy+qwry) +k(qxry−qyrx+rwqz+qwrz) +qwrw−qxrx−qyry−qzrz==(qv×rv+rwqv+qwrv,qwrw−qv⋅rv).(4.29)
从该公式中可以看出,我们同时使用了叉积和点积计算两个四元数的乘积。有了四元数的定义之后,我们就可以定义四元数的加法,共轭,范数,以及单位化运算:
Addition(加法):Conjugate(共轭):Norm(范数):Identity(单位复数):q^+r^=(qv,qw)+(rv,rw)=(qv+rv,qw+rw).q^∗=(qv,qw)∗=−(qv,qw).n(q^)=q^q^∗−−−√=q^∗q^−−−√=qv⋅qv+q2w−−−−−−−−−−√=q2x+q2y+q2z+q2w−−−−−−−−−−−−−−√.i^=(0,1).(4.30)
在对n(\hat{\mathbf{q}}) = \sqrt{\hat{\mathbf{q}}\hat{\mathbf{q}}^*}进行简化(结果如上)之后,虚部都抵消了且只剩下一个实部。四元数的规范化有时也可以表示为|\hat{\mathbf{q}}| = n(\hat{\mathbf{q}})[808]。由以前运算的结果可以推导出四元数的乘法倒数,表示为 q^−1。倒数必须满足公式q^−1q^=q^q^−1=1(这正是乘法倒数的通用性质)。由四元数规范化运算的定义可以推出以下公式:
n(q^)2=q^q^∗⟺q^q^∗n(q^)2=1,(4.31)
由此可以得到如下的乘法倒数公式:
Inverse(倒数):q^−1=1n(q^)2q^∗.(4.32)
四元数的求导公式使用了标量乘法,这是由公式4.29中的乘法运算推导出来的:sq^=(0,s)(qv,qw)=(sqv,sqw),并且q^s=(qv,qw)(0,s)=(sqv,sqw),也就是说标量乘法满足交换律。
由四元数的定义可以推导出以下的运算法则:
Conjugate rules(共轭法则):(q^∗)∗(q^+r^)∗(q^r^)∗=q^,=q^∗+r^∗,=r^∗q^∗.(4.33)
Norm rules(范数法则):n(q^∗)(q^+r^)∗=n(q^),=q^∗+r^∗.(4.34)
Laws of Multiplication(乘法法则):Linearity(线性关系):Associativity(结合律):p^(sq^+tr^)(sp^+tq^)r^p^(q^r^)=sp^q^+tp^r^,=sp^r^+tq^r^.=(p^q^)r^.(4.35)
一个四元数 q^=(qv,qw)为单位四元数,也就是说 n(q^)=1。由此可以把 q^ 写成如下形式:
q^=(sinϕuq, cosϕ)=sinϕuq+cosϕ,(4.36)
对于某些三维向量 uq,有∥uq∥=1,因为
n(q^)=n(sinϕuq, cosϕ)=sin2ϕ(uq⋅uq)+cos2ϕ−−−−−−−−−−−−−−−−−−√=sin2ϕ+cos2ϕ−−−−−−−−−−−−√=1(4.37)
有且仅有uq⋅uq=1=∥uq∥2。在下一节我们将会看到,单位四元数非常适合用于高效的创建旋转和定向运算。但在些之前,将会介绍关于四元数的一些额外操作。
对于复数,一个二维的单位向量可以写成cosϕ+isinϕ=eiϕ。而对于复数,使用同样的方法表示为
q^=sinϕuq+cosϕ=eϕuq.(4.38)
由公式4.38可以得到单位四元数的对数和指数运算公式:
Logarithm(对数公式):Power(指数公式):log(q^)=log(eϕuq)=ϕuq,q^t=(sinϕuq+cosϕ)t=eϕuq=sin(ϕt)uq+cos(ϕt).(4.39)
我们现在开始研究四元数集的一个子类,单位四元数,即那些具有单位长度的四元数。有关单位四元数的最重要的性质是它们可以表示任意的三维旋转,并且这种表示方式极其紧凑简单。
现在我们描述四元数对于旋转和定向操作如此实用的原因。首先把一个点或向量p=(px py pz pw)T 4个坐标值存储到一个四元数 p^ 的四个分量中,并假设有一个单位四元数 q^=(sinϕuq,cosϕ)。则有
q^p^q^−1(4.40)
把四元数
p^(也就是点
p)围绕轴
uq 旋转
2ϕ 度。需要注意的是,由于
q^ 是一个单位四元数,有
q^−1=q^∗。图4.8中演示了该旋转操作,显示这种方法可以用于围绕任意轴旋转。
图4.8 使用单位四元数q^=(sinϕuq,cosϕ)表示旋转变换的示例。该变换表示围绕轴 uq旋转 2ϕ 弧度。
四元数 q^ 任何非0的多个实部也可以表示同样的变换,这意味着 q^ 和 −q^ 表示相同的旋转操作。也就是说对轴 uq 及实部 qw 取反,会创建一个与原始四元数旋转结果完全一样的四元数。同样还意味着从一个矩阵中提取出四元数可以是 q^ 或者 −q^。
给定两个单位四元数 q^ 和 r^,对四元数 p^(可以用于表示点 p) 首先使用 q^ 再使用 r^ 的串联运算,如果公式4.41所示:
r^(q^p^q^∗)r^∗=(r^q^)p^(r^q^)∗=c^p^c^∗.(4.41)
其中 c^=r^q^ 是一个单位四元数,表示单位四元数 q^ 和 r^的串联。
Matrix Conversion
由于一些系统使用硬件的方式实现矩阵乘法,并且这种矩阵乘法比公式4.40更加高效,因此我们需要一种方法用于把一个四元数变换成一个矩阵,反之亦然。把四元数 q^ 转换成矩阵 Mq,可以使用公式4.42表示
Mq=⎛⎝⎜⎜⎜⎜1−s(q2y+q2z)s(qxqy+qwqz)s(qxqz−qwqy)0s(qxqy−qwqz)1−s(q2x+q2z)s(qyqz+qwqx)0s(qxqz+qwqys(qyqz−qwqx1−s(q2x+q2y00)0)0)1⎞⎠⎟⎟⎟⎟.(4.42)
其中标量为
s=2/n(q^)。对于单位四元数,公式4.42可以简化为
Mq=⎛⎝⎜⎜⎜⎜1−2(q2y+q2z)2(qxqy+qwqz)2(qxqz−qwqy)02(qxqy−qwqz)1−2(q2x+q2z)2(qyqz+qwqx)02(qxqz+qwqy2(qyqz−qwqx1−2(q2x+q2y00)0)0)1⎞⎠⎟⎟⎟⎟.(4.43)
使用四元数构建了矩阵之后,没有任何三角函数需要计算了,因此在实际运算过程中这种变换过程是非常高效的。
反过来,从一个正交矩阵 Mq 变换为单位四元数 q^ 则需要更多运算。该运算过程的关键是由公式4.43中的矩阵得到的不同点:
mq21−mq12mq02−mq20mq10−mq01=4qwqx,=4qwqy,=4qwqz.(4.44)
这些公式所表示的含义是,如果
qw 是已知的,就可以计算向量
vq的值,因此可以推导出
q^。使用如下公式可以计算矩阵
Mq 的迹
tr(Mq)=4−2s(q2x+q2y+q2z)=4(1−q2x+q2y+q2zq2x+q2y+q2z+q2w)=4q2wq2x+q2y+q2z+q2w=4q2wn(q^).(4.45)
由此可以得到如下单位四元数的转换公式:
qw=12tr(Mq)−−−−−−√,qy=mq02−mq204qw,qx=mq21−mq124qw,qz=mq10−mq014qw.(4.46)
为了得到一个数值稳定的例程,需要避免除以较小的数。因此,首先设置t=q2w−q2x−q2y−q2z,由此可以得到如下公式
m00m11m22u=t+2q2x,=t+2q2y,=t+2q2z,=m00+m11+m22=t+2q2w.(4.47)
也就是说最大的
m00,m11,m22 和
u 值反过来可以确定
qx,qy,qz 和
qw 也是最大的。如果
qw 为最大值,则可以使用公式4.46除四元数。否则,我们需要注意以下公式:
4q2x4q2y4q2z4q2w=+m00−m11−m22+m33,=−m00+m11−m22+m33,=−m00−m11+m22+m33,=tr(Mq).(4.48)
然后使用上面适用的公式计算
qx,qy 和
qz 的最大值,之后再使用公式4.44计算四元数
q^ 的其余分量值。幸运是已经有相关的代码用于转换的计算,见本章末尾的Further Reading and Resources。
Spherical Linear Interpolation
Spherical Linear Interpolation(球面线性插值)是指,给定两个单位四元数 q^ 和 r^,以及一个参数 t∈[0,1],计算一个插值四元数的操作。比如在处理动画物体时这种操作非常实用。但是对于相机朝向的插值运算该操作并不实用,因为相机的“向上方向”的向量在插值过程中会发生倾斜,通常产生一种令人不快的效果。
这种插值操作的代数形式可以使用如下的组合四元数 s^ 表示:
s^(q^,r^,t)=(r^q^−1)tq^.(4.49)
但是对于使用软件实现来说,使用以下的形式更合适,其中
slerp 表示球面线性插值:
s^(q^,r^,t)=slerp(q^,r^,t)=sin(ϕ(1−t))sinϕq^=sin(ϕt)sinϕr^.(4.50)
要计算该公式中所需要的 ϕ 值,可以使用如下论据:cosϕ=qxrx+qyry+qzrz+qwrw[224]。对于任意的 t∈[0,1],slerp函数用于计算插值的两个四元数从 q^(t=0) 到 r^(t=1) 一起构成了四维单位球面上的最短圆弧。该弧线位于由 q^、r^及球心所有的平面,与四维单位球的相交形成的圆圈上。如图4.9所示。由些计算得到的旋转四元数以恒定的速度围绕一个固定轴旋转。这种具有恒定速度的旋转曲线, 加速度为0,称为 geodesic 曲线[263]。
slerp函数非常适用于在两个朝向向量之间进行插值,并且计算结果非常稳定(保持固定轴,恒定速度)。在使用多个欧拉角度进行插值时无法达到这种运算效果。在实际运算过程中,直接执行slerp运算需要调用大量的三角函数,是一种高计算成本的操作。为此,Li[771,772]提出改进的方法,在不牺牲任何精度的情况下可以更快速的计算slerps值。
当有多个表示朝向的四元数可以用时,比如q^0,q^1,⋅⋅⋅,q^n−1,并且我们要从 q^0 到 q^1 再到 q^2 进行插值,依此类推,直接到 q^n−1,可以使用一连串的slerp运算。假设现在要计算 q^i,我们将会使用 q^i−1 和 q^i 作为slerp的参数。计算完 q^i 之后,就使用 q^i 和 q^i+1 作为下一个slerp运算的参数。这会导致在对朝向向量进行插值过程中出现急剧的变动,如图4.9所示。这种方法类似于对多个点进行线性插值;详见原书578页图13.2的右上图。一些读者在阅读第13章有关样条曲线的描述之后可能会回过头来学习以下段落。
一种更好的插值计算方法是使用某种样式的样条曲线。首先我们引入介于 q^i 和 q^i+1 之间的四元数 a^i 和 a^i+1。然后使用 q^i,a^i,a^i+1 和 q^i+1 组成的四元数集定义三次球面插值运算。令人惊奇的是,使用如下的公式可以计算这些额外的四元数[294]:
a^i=q^iexp[−log(q^−1iq^i−1)+log(q^−1iq^i+1)4].(4.51)
其中
q^i 和
a^i 用于对四元数进行球面插值,使用公式4.52所示的光滑的三次样条曲线:
saurd(q^i,q^i+1,a^i,a^i+1,t)=slerp(slerp(q^i,q^i+1,t),slerp(a^i,a^i+1,t),2t(1−t)).(4.52)
如上公式所示,slerp 函数由多次重复使用slerp进行球面插值运算构成(详见第13章13.1.1节有关对多个点进行重复线性插值的讲解)。插值将会在初始的朝向四元数 q^i,i∈[0,⋅⋅⋅,n−1]之间进行传递,但是不会在 a^i 中传递,这些新引入的四元数用于表示初始四元数的切线方向。
插值将通过传递
初始方向 ˆqi,我 ∈ [0,…,n−1],但不是通过 ˆai — — 这些都是
用于指示在初始取向的切线方向