原文链接:http://www.nicemxp.com/articles/30
四元数在3d图形学中主要用来进行球面线性插值,可以让相机在球面上圆滑的移动,避免了相机通过欧拉角计算的一些弊端,如万向死锁。所以记录下四元数的一些知识和想法,本文只是一段自己学习过程中的理解思路,可能有些不理解的地方自己理解的也有问题,具体的推导过程太多这里不记录,可以自己根据性质推导或者查看相关资料。
四元数在数学上是一种超复数,在我们实际中没有实际意义。因为自己的学习习惯是学习事物总喜欢跟现实关联来帮助理解学习,所以学习四元数时想通过现实中的一些事实来跟四元数联想关联,最后发现都是不对的,浪费了很多时间,所以四元数更多的是理论上的意义,数学上的意义,而不是现实中有什么物理意义(可能有只是自己没想到)。
简单的复数如 p = 5 + 3i,其中的5是实部,3i是虚部,但是有 i*i = -1。
四元数是一个超复数所以可以记为 q = w + x * i + y * j + z * k
其中w为超复数q的实部,x * i + y * j + z * k 为q的虚部
可以将q的虚部看作是一个3维空间的向量,所以四元数的记法可以写成q(w, x, y, z)或者q(w, v),其中v是向量
因为i,j,k是一个超复数,所以有复数的性质:i*i = j*j = k*k = -1
x * i + y * j + z * k也可以看作是一个3维向量空间中的向量,所以有性质:i * j = k = - j * i,j * k = i = -k * j,k * i = j = -i * j 可以看做是向量的叉乘。
有了以上性质我们可以推断出四元数的乘法公式:
p = p0 + p1 * i + p2 * j + p3 * k => 也可以表示为p = p0 + pv(向量表示法pv为(p1i,p2j,p3k))
q = q0 + q1 * i + q2 * j + q3 * k => 也可以表示为q = q0 + qv(向量表示法qv为(q1i,q2j,q3k))
p * q = (p0 + p1 * i + p2 * j + p3 * k) * (q0 + q1 * i + q2 * j + q3 *k)
可以在草稿纸上自行展开,然后利用上面的性质可以得出结果:p * q = (p0 * q0 - (pv · qv) + (p0 * qv + q0 * pv + pv x qv))
其中粗体部分是向量,点乘和叉乘要分清楚
有了四元数的乘法公式我们可以继续得到很多有用的性质。
首先是四元数p的共轭四元数p*,他俩之间有性质:p 乘 (p*) = |p| (|p|表示p的模长,为(p0^2 + p1^2 + p2^2 + p3^2)开平方)
根据该性质和上面的乘法公式我们可以得到结果四元数p(p0 + p1 * i + p2 * j + p3 * k)的共轭四元数p*为(p0 - p1 * i - p2 * j - p3 * k)
有了共轭四元数p*我们可以来看四元数p的逆 p^(-1),因为p * p^(-1) = 1,p * (p*) = |p|,所以有p^(-1) = p* / |p|
四元数q为单位四元数时|q| = 1
还有一些不常用的性质:
四元数q = (cos(α), sin(α) * n) 那么它的对数公式为 log(q) = (0, α* n)
四元数q = (0, α* n),那么它的指数公式为 exp(q) = (cos(α), sin(α) * n)
四元数的t次幂(可以理解为对四元数表示的角位移进行t插值),q = (cos(α), sin(α) * n),q^t = exp(t*log(q)) = (cos(tα), sin(tα) * n)(使用上面的公式推导出的结果,可以看到t值直接作用于角度α,所以跟球面插值一样)
有了这些性质,我们来说下四元数复杂难理解为什么我们要用,因为四元数的格式,或者说通过四元数这种描述方式可以用来计算球面旋转,球面线性插值,但是不是说四元数有什么实际物理意义可以表述为球面旋转或者球面线性插值,而是数学上的,或者说是通过某某些公式最后得到的结果,就是恰好就是旋转的结果和插值的结果,下面我们具体说下四元数是如何来计算旋转和球面插值的。
球面旋转:
球面上的两个点a和b,物体在a点圆滑过渡到达b点,实际上可以理解为物体在点a经过一个轴,绕轴旋转一定得角度到达了b点。所以在球面上的旋转我们可以用一个轴和一个角的方式来表达,向量n和旋转角θ,(n, θ)被称作为一个轴角对,因为轴的长度无关紧要所以为了方便向量n为单位向量,通过一个轴角对我们可以描述一段球面旋转操作。然而通过四元数的形式也可以来描述一段球面旋转,一个点绕一个轴n旋转θ用四元数表示为q(cos(θ/2), sin(θ/2) * n)或者(cos(θ/2), sin(θ/2)*ni,sin(θ/2)*nj, sin(θ/2)*nk),其中轴向量n是单位向量,q也为单位四元数。我们可以把表示旋转的四元数q称为一个角位移,为什么这样表示的四元数就可以表示一段球面旋转或者说角位移呢,因为在数学意义上,将三维上的点p(x, y, z)拓展到四维用四元数表示为p(0, x, y, z),根据四元数的性质和乘法公式有 p' = q^(-1) * p * q ,点p经过四元数q和q的逆相乘(注意超复数不满足乘法交换律),按照公式上得到的结果p'在数学上的结果就是点p绕着轴n旋转了θ后的点的坐标。所以这就是为什么四元数q(cos(θ/2), sin(θ/2) * n)可以用来表示球面旋转或者说角位移,如果不理解这个公式的话可以死记,如果想为了更好的理解可以去学习下一个点绕任意轴旋转的矩阵公式,这个是可以通过物理意义理解得出的结果,然后回来将这个公式带入向量n和角度θ,会跟矩阵公式一样。但是他们之间的对应只是理论上的意义,没有什么实践意义。
球面插值slerp:
球面插值是在两个角位移之间进行插值,可以得到在球面上均匀光滑的球面移动。
首先我们看正常思路:球面上有两个角位移pa和pb,他们表示的是两个角位移并且有相同的轴n,pa 和pb这两个角位移之间在球面上会有一个角位移来使pa平滑转动到pb,设这个角位移为q,所以就有pb = pa * q,那么这个角位移q是多少呢,因为pa是一个四元数所以等式两边同时乘以pa的逆pa^(-1),因为pa*pa^(-1) = 1,所以q = pa^(-1)*pb,这样我们就得到了角位移q,所以我们有了角位移可以对角位移进行插值,用数值t表示插值的范围,0 =< t <= 1,上面介绍性质的时候说过,四元数q的t次幂,指数t直接作用于角度上,所以q^t就是对q进行插值,这样我们得到插值后的角位移q',pa * p'就是插值得到的最后角位移,最后我们可以得到理论上的slerp插值公式:
slerp(qa, qb, t) = qa * (((qa ^ (-1)) * qb) ^ t)
下面我们看另一种插值计算方式: 两个角位移,四元数qa, qb
qa(cos(θ/2),sin(θ/2) * n),qb(cos(θ'/2), sin(θ'/2) * n)
qa和qb分别表示了两个角位移,对qa和qb两个角位移之间进行插值,有公式:
slerp(qa, qb, t) = (sin( ( 1 - t ) * Ω ) * qa + sin(t*Ω) * qb) / sin(Ω)
这个公式是从二维空间推导拓展到四元数中的,推导过程可以看相关资料。
公式中:
slerp(qa, qb, t)为插值四元数
qa =四元数a(要插入的第一个四元数)
qb =四元数b(要插入的第二个四元数)
t = 0.0(在qa)和1.0(在qb)之间的标量
Ω是qa和qb之间角度的一半
我们先来求Ω,根据性质可以推导出cos(Ω) = qa · qb = wa * wb + xa * xb + ya * yb + za * zb,就是两个四元数的点乘,这个证明方式有很多,可以令θ' = θ + α,就有qa = (cos(θ/2),sin(θ/2)*n),qb = (cos(θ/2 + α/2),sin(θ/2 + α/2)*n),其中n为单位向量,qa·qb = cos(θ/2) * cos(θ/2 + α/2) + sin(θ/2) * sin(θ/2 + α/2),展开后就有qa·qb = cos(α / 2)。或者我们也可以换个角度考虑,因为qb = qa * q,角位移q就表示了从qa球面旋转到qb,所以cos(Ω)就是角位移q的实部,通过上面我们已经知道q = qa^(-1) * qb,四元数qa(wa, xa, ya, za), qb(wb, xb, yb, zb),所以qa^(-1)为(wa, -xa, -ya, -za),根据四元数的乘法公式,qa^(-1) * qb的实数部分 等于wa * wb + xa * xb + ya * yb + za * zb,所以证毕。
知道了Ω后,根据t的数值我们就可以计算出插值后的四元数了,但是还有一点需要注意,3d中物体从qa平滑过渡到qb,因为是在球体上,所以会有两个路径到达qb,所以当qa与qb之间的夹角大于180度时也就是cos(Ω)小于0时,得到的插值会让物体沿着远路径过渡到qb,所以当夹角大于180度时(cos(Ω)小于0)时,为了走近路径让qa反方向过渡到qb,我们可以看下qb(cos(θ'/2), sin(θ'/2) * n),qb本身是一段角位移,一段绕n轴旋转了θ'度的旋转,qb本身也有两个路径,另一个路径可以为-(360-θ'),所以另一个路径的四元数表示法为qb'( cos(θ' - 180), sin(θ' - 180) * n),整理后会有qb'( -cos(θ' ), -sin(θ' ) * n),也就是qb的负四元数,所以当qa到qb的夹角大于180度时,避免走长路径,可以用qa到qb'过渡,因为qb和qb'表示的角位移是一样的,因为qb'是qb的负四元数,所以qa和qb'的夹角一定小于180度,这时cos(Ω)一定大于0,这样走的就是短路径了。
以上这些是自己对四元数的理解,只是为了记录下自己对四元数的理解过程,省略了很多东西,只是记载了自己学习过程中卡壳的地方,有些可能是有问题或者错误的,还需要以后来慢慢的改正。
slerp的替代方法,nlerp