四元数学习记录

前言

        如果你去看网络上的游戏开发教程,他们大都告诉你四元数是用来防止欧拉角的万向锁。但是他们都不会去深究或者说讲明。因为四元数确实很困难,但是在开发的过程中,我们确实需要一些关于四元数的知识来为我们的开发做理论基础。这篇文章,我只说明我自己所了解的部分,也算是我对于学习四元数的阶段总结。

        特别说明,本文全部的计算都按照右手坐标系法则,所有的旋转都是逆时针旋转。

正文

        四元数(Quaternion)是一种扩展复数的数学概念,它可以用来表三维空间中的旋转。它由威廉·罗恩·哈密顿于1843年提出,形式为:

\[q=a+bi+cj+dk\]

其中,\(a,b,c,d\)是实数。\(i,j,k\)是虚数单位,满足以下乘法规则:

\[i^2=j^2=k^2=ijk=-1\]

四元数的实部\(a\)、虚部为\(bi,cj,dk\),所以也有下面这样的表示:

\[q = (a,\vec{v})\]

其中,\(\vec{v}=(b,c,d)\)是三维向量(在我看到的资料中也有\(q = (\vec{v},a)\)的表示,我并不想去深究那个是对的,反正大家懂什么意思就好了)。

四元数的运算

        我们设有两个四元数\(q_1 = a_1 + b_1i + c_1j + d_1k\)\(q_2= a_2 + b_2i + c_2j + d_2k\),和一个通用的四元数:\(q = a + bi + cj + dk\)

加法

        两个四元数的加法可以表示为

\[q_3 = q_1 + q_2 = (a_1 + a_2) + (b_1 + b_2)i + (c_1 + c_2)j + (d_1 + d_2)k\]

乘法

        两个四元数的乘法可以表示为

\[\begin{aligned} q_3 = q_1 \cdot q_2 = & a_1a_2 + a_1b_2i + a_1c_2j + a_1d_2k + \\& a_2b_1i + b_1b_2i\times i + b_1c_2i\times j + b_1d_2i\times k + \\& a_2c_1j + b_2c_1j\times i + c_1c_2j\times j + c_1d_2j\times k + \\& a_2d_1k + b_2d_1k\times i + c_1d_1k\times j + d_1d_2k\times k \end{aligned}\]

接下来,我们代入下面的公式

\[i^2=j^2=k^2=ijk=-1\]

\[i\times j=k,j\times k=i,k\times j=i\]

\[i\times j=k,j\times k=i,k\times i=j\]

\[j\times i=-k,k\times j=-i,i\times k=-j\]

所以上面的公式可简化为如下所示:

\[\begin{aligned} q_3 = q_1 \cdot q_2 = & a_1a_2 + a_1b_2i + a_1c_2j + a_1d_2k + \\& a_2b_1i - b_1b_2 + b_1c_2k - b_1d_2j + \\& a_2c_1j - b_2c_1k - c_1c_2 + c_1d_2i + \\& a_2d_1k + b_2d_1j - c_1d_1i - d_1d_2 \\=&\\&(a_1a_2 - b_1b_2 - c_1c_2 - d_1d_2) +\\&(a_1b_2 + a_2b_1 + c_1d_2 - c_1d_2)i+\\&(a_1c_2 - b_1d_2 + a_2c_1 + b_2d_1)j+\\&(a_1d_2 + b_1c_2 - b_2c_1 + a_2d_1)k \end{aligned}\]

        因为乘法的运算涉及到叉乘,所以四元数的乘法是不支持交换律的。但是四元数和标量之间的运算是满足交换律的。之前我们有说过四元数有另外的一种形式:\(q = (a,\vec{v}),\vec{v} = (b,c,d)\)所以四元数乘法也可以如下表示:

\[q_1 \cdot q_2 = (a_1*a_2 - \vec{v_1} \cdot \vec{v_2}\space,\space a_1\vec{v_2} + a_2\vec{v_1} + \vec{v_1} \times \vec{v_2})\]

而单位四元数和单位四元数相乘,最终的结果一定也是一个单位四元数。大家可以将上面乘法结果的式子进行验算便可以得到这个结论。

共轭

\[q^* = a-bi-cj-dk\]

\[|q| = \sqrt{a^2+b^2+c^2+d^2}\]

\[q^{-1} = \frac{q^*}{|q|^2}\]

单位四元数

        单位四元数即模长为1的四元数。按前文所述,我们知道四元数其实可以写做是\(q = (a,\vec{v})\),那么对于一些特殊的单位四元数而言,我们可以写成\(q = (\cos{\theta},\sin{\theta}\vec{u})\)其中\(\vec{u}\)的单位向量。

四元数的旋转表达

        有了前面的知识作为铺垫,那么我就可以来探索四元数如何表达旋转了。首先我们先思考一下,如果我们不用四元数,如何获取三维空间下向量\(\vec{v}\)绕着向量\(\vec{u}\)旋转\(\theta\)角度后的向量\(\vec{v'}\)呢?下面在计算的时候会认为\(\vec{u}\)是单位向量。

Rodrigues(罗德里格斯)旋转公式

        我们先进行如下拆分,我们将\(\vec{v}\)拆分为两个变量\(\vec{v_{\perp}}\)\(\vec{v_{\parallel}}\)。其中\(\vec{v_{\parallel}}\)\(\vec{v}\)\(\vec{u}\)上的投影,\(\vec{v_{\perp}}\)\(\vec{v}\)垂直于\(\vec{u}\)上的分量。它们满足如下的关系:

\[\vec{v_{\parallel}} = (\vec{v} \cdot \vec{u}) / |u| * \vec{u}\]

\[\vec{v_{\perp}} = \vec{v} - \vec{v_{\parallel}}\]

因为\(\vec{u}\)是单位向量,所以上面的方程我们可以简化为:

\[\vec{v_{\parallel}} = (\vec{v} \cdot \vec{u})\vec{u}\]

显然当向量\(\vec{v}\)绕着向量\(\vec{u}\)旋转\(\theta\)角度时,\(\vec{v_{\parallel}}\)并不会发生变换。所以我们只要考虑\(\vec{v_{\perp}}\)的变化即可。我们设\(\vec{v_{\perp}}\)变化后的向量为\(\vec{v'_{\perp}}\) ,那么这个向量一定存在于向量\(\vec{v_{\perp}}\)和向量 \(\vec{u}\times\vec{v_{\perp}}\) 所构成的平面内。且这个向量必然和 \(\vec{v_{\perp}}\) 的夹角为 \(\theta\)。所以我们可以得到这个向量的表达式。我们设这个向量为 \(\vec{v_{\perp\theta}}\)。这其表达式如下所示:

\[\vec{v_{\perp\theta}} = \vec{v_{\perp}} \cos{\theta} + \vec{u}\times\vec{v_{\perp}} \sin{\theta}\]

这里需要注意的是\(\vec{v_{\perp}}\times\vec{u}\)\(\vec{u}\times\vec{v_{\perp}}\)方向上的区别会导致角度的改变。

最后我们将\(\vec{v_{\parallel}}\)\(\vec{v_{\perp\theta}}\)的相加便得到\(\vec{v'}\)。表达式如下:

\[\vec{v'} = \vec{v_{\parallel}} + \vec{v_{\perp\theta}}\]

然后我们将之前的得到的公式代入并化简:

\[ \begin{aligned} \vec{v'} =& \vec{v_{\parallel}} + \vec{v_{\perp}} \cos{\theta} + \vec{u}\times\vec{v_{\perp}} \sin{\theta}\\=&\vec{v_{\parallel}} + (\vec{v} - \vec{v_{\parallel}})\cos{\theta}+\vec{u}\times(\vec{v} - \vec{v_{\parallel}}) \sin{\theta}\\=&(\vec{v} \cdot \vec{u})\vec{u}+(\vec{v} - (\vec{v} \cdot \vec{u})\vec{u})\cos{\theta}+\vec{u}\times(\vec{v} - (\vec{v} \cdot \vec{u})\vec{u}) \sin{\theta} \end{aligned}\]

因为叉乘满足分配律和根据叉乘自身为0的性质,我们可以得到:

\[\begin{aligned} \vec{v'} =&(\vec{v} \cdot \vec{u})\vec{u}+(\vec{v} - (\vec{v} \cdot \vec{u})\vec{u})\cos{\theta}+(\vec{u}\times\vec{v} - (\vec{v} \cdot \vec{u})\vec{u}\times\vec{u}) \sin{\theta}\\=&(\vec{v} \cdot \vec{u})\vec{u}+(\vec{v} - (\vec{v} \cdot \vec{u})\vec{u})\cos{\theta}+\vec{u}\times\vec{v} \sin{\theta}\\=&(\vec{v} \cdot \vec{u})\vec{u}+\vec{v}\cos{\theta} - (\vec{v} \cdot \vec{u})\vec{u}\cos{\theta}+\vec{u}\times\vec{v} \sin{\theta}\\=&\vec{v}\cos{\theta} + (\vec{v} \cdot \vec{u})\vec{u}(1-\cos{\theta}) + \vec{u}\times\vec{v} \sin{\theta}\end{aligned}\]

而最终我们所得到的公式有一个称呼:Rodrigues(罗德里格斯)旋转公式。它计算三维空间中,一个向量绕旋转轴旋转给定角度以后得到的新向量的计算公式。之所以我先介绍这个公式,是因为后面四元数在进行计算后最终得到的结果便是这个公式的形式。

四元数的旋转公式

        通过四元数表达的旋转公式如下所示:

\[\vec{v'} = q\vec{v}q^{-1}\]

其中\(q\)是四元数,\(q^{-1}\)\(q\)的逆。我们将\(\vec{u}\)设定为单位向量,如果后面旋转轴非单位向量,那么在计算的时候需要先将其归一化。那么此时四元数,我们就可以设定为\(q = (\cos{\frac{\theta}{2}} \space,\space\vec{u}\sin{\frac{\theta}{2}})\),此时\(q\)的模长为1,所以\(q^{-1} = q^*=(\cos{\frac{\theta}{2}} \space,\space -\vec{u}\sin{\frac{\theta}{2}})\)的。因为是关于四元数的运算,所以我们要将向量转换为四元数。向量在四元数中表示为实部为零的四元数,即\(\vec{v}=(0,x_vi,y_vj,z_vk)\),即\(\vec{v}=(0,\vec{v})\)。接下来,我们进行四元数的运算,因为四元数并不支持乘法交换律,所以我们先运算\(q\vec{v}\)。计算结果如下:

\[q_1 \cdot q_2 = (a_1*a_2 - \vec{v_1} \cdot \vec{v_2}\space,\space a_1\vec{v_2} + a_2\vec{v_1} + \vec{v_1} \times \vec{v_2})\]

\[\begin{aligned}q\vec{v}=&(\cos{\frac{\theta}{2}}\space,\space\vec{u}\sin{\frac{\theta}{2}})(0\space,\space\vec{v})\\=&(\cos{\frac{\theta}{2}} * 0 - \vec{v}\cdot\vec{u}\sin{\frac{\theta}{2}}\space,\space\cos{\frac{\theta}{2}}\vec{v} + 0*\vec{u}\sin{\frac{\theta}{2}}+\vec{u}\times\vec{v}\sin{\frac{\theta}{2}})\\=&(- \vec{v}\cdot\vec{u}\sin{\frac{\theta}{2}}\space,\space\cos{\frac{\theta}{2}}\vec{v} +\vec{u}\times\vec{v}\sin{\frac{\theta}{2}})\end{aligned}\]

接下来我们将上面的结果代入\(q\vec{v}q^{-1}\)

\[\begin{aligned}q\vec{v}q^{-1}=&(-\vec{v}\cdot\vec{u}\sin{\frac{\theta}{2}}\space,\space\cos{\frac{\theta}{2}}\vec{v} +\vec{u}\times\vec{v}\sin{\frac{\theta}{2}})(\cos{\frac{\theta}{2}} \space,\space -\vec{u}\sin{\frac{\theta}{2}})\\=&(-\vec{v}\cdot\vec{u} * \cos{\frac{\theta}{2}}*\sin{\frac{\theta}{2}} - (\cos{\frac{\theta}{2}}\vec{v} +\vec{u}\times\vec{v}\sin{\frac{\theta}{2}})\cdot(-\vec{u}\sin{\frac{\theta}{2}})\space,\space-\vec{v}\cdot\vec{u}\sin{\frac{\theta}{2}}(-\vec{u}\sin{\frac{\theta}{2}})+\cos{\frac{\theta}{2}}(\cos{\frac{\theta}{2}}\vec{v} +\vec{u}\times\vec{v}\sin{\frac{\theta}{2}})+(\cos{\frac{\theta}{2}}\vec{v} +\vec{u}\times\vec{v}\sin{\frac{\theta}{2}})\times(-\vec{u}\sin{\frac{\theta}{2}}))\end{aligned}\]

因为点乘和叉乘都满足分配律,再根据叉乘的自身为0的性质和\(-\vec{v}\times\vec{u} = \vec{u}\times\vec{v}\)所以我们可以得到:

\[\begin{aligned}q\vec{v}q^{-1}=&(-\vec{v}\cdot\vec{u} * \cos{\frac{\theta}{2}}*\sin{\frac{\theta}{2}} - (\cos{\frac{\theta}{2}}\vec{v} +\vec{u}\times\vec{v}\sin{\frac{\theta}{2}})\cdot(-\vec{u}\sin{\frac{\theta}{2}})\space,\space-\vec{v}\cdot\vec{u}\sin{\frac{\theta}{2}}(-\vec{u}\sin{\frac{\theta}{2}})+\cos{\frac{\theta}{2}}(\cos{\frac{\theta}{2}}\vec{v} +\vec{u}\times\vec{v}\sin{\frac{\theta}{2}})+(\cos{\frac{\theta}{2}}\vec{v} +\vec{u}\times\vec{v}\sin{\frac{\theta}{2}})\times(-\vec{u}\sin{\frac{\theta}{2}}))\\=&(-\vec{v}\cdot\vec{u} * \cos{\frac{\theta}{2}}*\sin{\frac{\theta}{2}} + \cos{\frac{\theta}{2}}\vec{v}\cdot\vec{u}\sin{\frac{\theta}{2}}+(\vec{u}\times\vec{v}\sin{\frac{\theta}{2}})\cdot\vec{u}\sin{\frac{\theta}{2}}\space,\space(\vec{v}\cdot\vec{u})\vec{u}(\sin{\frac{\theta}{2}})^2+(\cos{\frac{\theta}{2}})^2\vec{v}+\vec{u}\times\vec{v}\cos{\frac{\theta}{2}}*\sin{\frac{\theta}{2}}-\cos{\frac{\theta}{2}}\vec{v}\times\vec{u}\sin{\frac{\theta}{2}} -(\vec{u}\times\vec{v}\sin{\frac{\theta}{2}})\times\vec{u}\sin{\frac{\theta}{2}}) \\=&(-\vec{v}\cdot\vec{u} * \cos{\frac{\theta}{2}}*\sin{\frac{\theta}{2}} + \vec{v}\cdot\vec{u}\cos{\frac{\theta}{2}}*\sin{\frac{\theta}{2}}+(\vec{u}\times\vec{v})\cdot\vec{u}(\sin{\frac{\theta}{2}})^2\space,\space(\vec{v}\cdot\vec{u})\vec{u}(\sin{\frac{\theta}{2}})^2+(\cos{\frac{\theta}{2}})^2\vec{v}+\vec{u}\times\vec{v}\cos{\frac{\theta}{2}}*\sin{\frac{\theta}{2}}+\vec{u}\times\vec{v}\cos{\frac{\theta}{2}}*\sin{\frac{\theta}{2}} -(\vec{u}\times\vec{v})\times\vec{u}(\sin{\frac{\theta}{2}})^2)\\=&((\vec{u}\times\vec{v})\cdot\vec{u}(\sin{\frac{\theta}{2}})^2\space,\space(\vec{v}\cdot\vec{u})\vec{u}(\sin{\frac{\theta}{2}})^2+(\cos{\frac{\theta}{2}})^2\vec{v}+\vec{u}\times\vec{v}*2*\cos{\frac{\theta}{2}}*\sin{\frac{\theta}{2}} -(\vec{u}\times\vec{v})\times\vec{u}(\sin{\frac{\theta}{2}})^2)\end{aligned}\]

根据叉乘的性质,我们可以知道\((\vec{u}\times\vec{v})\)\(\vec{u}\)互相垂直,所以他们的点积结果为0。所以上面的式子可以化简为:

\[\begin{aligned}q\vec{v}q^{-1}=&((\vec{u}\times\vec{v})\cdot\vec{u}(\sin{\frac{\theta}{2}})^2\space,\space(\vec{v}\cdot\vec{u})\vec{u}(\sin{\frac{\theta}{2}})^2+(\cos{\frac{\theta}{2}})^2\vec{v}+\vec{u}\times\vec{v}*2*\cos{\frac{\theta}{2}}*\sin{\frac{\theta}{2}} -(\vec{u}\times\vec{v})\times\vec{u}(\sin{\frac{\theta}{2}})^2)\\=&(0\space,\space(\vec{v}\cdot\vec{u})\vec{u}(\sin{\frac{\theta}{2}})^2+(\cos{\frac{\theta}{2}})^2\vec{v}+\vec{u}\times\vec{v}*2*\cos{\frac{\theta}{2}}*\sin{\frac{\theta}{2}} -(\vec{u}\times\vec{v})\times\vec{u}(\sin{\frac{\theta}{2}})^2)\end{aligned}\]

接下来我们使用二重叉积公式:\((\vec{a}\times\vec{b})\times\vec{c} = (\vec{a}\cdot\vec{c})\vec{b} - (\vec{b}\cdot\vec{c})\vec{a}\),将上面的式子改写为:

\[\begin{aligned}q\vec{v}q^{-1}=&(0\space,\space(\vec{v}\cdot\vec{u})\vec{u}(\sin{\frac{\theta}{2}})^2+(\cos{\frac{\theta}{2}})^2\vec{v}+\vec{u}\times\vec{v}*2*\cos{\frac{\theta}{2}}*\sin{\frac{\theta}{2}} -(\vec{u}\times\vec{v})\times\vec{u}(\sin{\frac{\theta}{2}})^2)\\=&(0\space,\space(\vec{v}\cdot\vec{u})\vec{u}(\sin{\frac{\theta}{2}})^2+(\cos{\frac{\theta}{2}})^2\vec{v}+\vec{u}\times\vec{v}*2*\cos{\frac{\theta}{2}}*\sin{\frac{\theta}{2}} -((\vec{u}\cdot\vec{u})\vec{v} - (\vec{v}\cdot\vec{u})\vec{u})(\sin{\frac{\theta}{2}})^2)\\=&(0\space,\space(\vec{v}\cdot\vec{u})\vec{u}(\sin{\frac{\theta}{2}})^2+(\cos{\frac{\theta}{2}})^2\vec{v}+\vec{u}\times\vec{v}*2*\cos{\frac{\theta}{2}}*\sin{\frac{\theta}{2}} -(\vec{u}\cdot\vec{u})\vec{v}(\sin{\frac{\theta}{2}})^2 + (\vec{v}\cdot\vec{u})\vec{u}(\sin{\frac{\theta}{2}})^2)\\=&(0\space,\space(\vec{v}\cdot\vec{u})\vec{u}*2*(\sin{\frac{\theta}{2}})^2+(\cos{\frac{\theta}{2}})^2\vec{v}+\vec{u}\times\vec{v}*2*\cos{\frac{\theta}{2}}*\sin{\frac{\theta}{2}} -(\vec{u}\cdot\vec{u})\vec{v}(\sin{\frac{\theta}{2}})^2)\end{aligned}\]

因为\(\vec{u}\)是单位向量,所以\((\vec{u}\cdot\vec{u})=1\),所以上面的式子可以简化为:

\[\begin{aligned}q\vec{v}q^{-1}=&(0\space,\space(\vec{v}\cdot\vec{u})\vec{u}*2*(\sin{\frac{\theta}{2}})^2+(\cos{\frac{\theta}{2}})^2\vec{v}+\vec{u}\times\vec{v}*2*\cos{\frac{\theta}{2}}*\sin{\frac{\theta}{2}} -(\vec{u}\cdot\vec{u})\vec{v}(\sin{\frac{\theta}{2}})^2)\\=&(0\space,\space(\vec{v}\cdot\vec{u})\vec{u}*(2*(\sin{\frac{\theta}{2}})^2 - 1 + 1)+(\cos{\frac{\theta}{2}})^2\vec{v}+\vec{u}\times\vec{v}*2*\cos{\frac{\theta}{2}}*\sin{\frac{\theta}{2}} -\vec{v}(\sin{\frac{\theta}{2}})^2)\\=&(0\space,\space(\vec{v}\cdot\vec{u})\vec{u}*(2*(\sin{\frac{\theta}{2}})^2 - 1 + 1)+((\cos{\frac{\theta}{2}})^2 - (\sin{\frac{\theta}{2}})^2)\vec{v}+\vec{u}\times\vec{v}*2*\cos{\frac{\theta}{2}}*\sin{\frac{\theta}{2}})\end{aligned}\]

这里你会发现我把部分的式子变得复杂起来,不过这都是为了后续化简。我们通过三角公式可知:\(1 = (\cos{\alpha})^2 + (\sin{\alpha})^2\)。而我们从倍角公式可知:

\[\sin{2\alpha} = 2\sin{\alpha}\cos{\alpha}\] \[\cos{2\alpha} = (\cos{\alpha})^2 - (\sin{\alpha})^2\]

将上述式子代入,我们可以得到:

\[ \begin{aligned} q\vec{v}q^{-1}=&(0\space,\space(\vec{v}\cdot\vec{u})\vec{u}*(2*(\sin{\frac{\theta}{2}})^2 - 1 + 1)+((\cos{\frac{\theta}{2}})^2 - (\sin{\frac{\theta}{2}})^2)\vec{v}+\vec{u}\times\vec{v}*2*\cos{\frac{\theta}{2}}*\sin{\frac{\theta}{2}})\\\\=&(0\space,\space(\vec{v}\cdot\vec{u})\vec{u}*(2*(\sin{\frac{\theta}{2}})^2 - ((\sin{\frac{\theta}{2}})^2 + (\cos{\frac{\theta}{2}})^2) + 1)+\cos{\theta}\vec{v}+\vec{u}\times\vec{v}*\sin{\theta})\\\\=&(0\space,\space(\vec{v}\cdot\vec{u})\vec{u}*(2*(\sin{\frac{\theta}{2}})^2 - (\sin{\frac{\theta}{2}})^2 - (\cos{\frac{\theta}{2}})^2+ 1)+\cos{\theta}\vec{v}+\vec{u}\times\vec{v}*\sin{\theta})\\\\=&(0\space,\space(\vec{v}\cdot\vec{u})\vec{u}*((\sin{\frac{\theta}{2}})^2 - (\cos{\frac{\theta}{2}})^2+ 1)+\cos{\theta}\vec{v}+\vec{u}\times\vec{v}*\sin{\theta})\\\\=&(0\space,\space(\vec{v}\cdot\vec{u})\vec{u}*(1 - ((\cos{\frac{\theta}{2}})^2 - (\sin{\frac{\theta}{2}})^2))+\cos{\theta}\vec{v}+\vec{u}\times\vec{v}*\sin{\theta})\\\\=&(0\space,\space(\vec{v}\cdot\vec{u})\vec{u}*(1 - \cos{\theta})+\cos{\theta}\vec{v}+\vec{u}\times\vec{v}*\sin{\theta}) \end{aligned}\]

此时你会发现虚部的值正是罗德里格斯(Rodrigues)旋转公式:

\[\vec{v}\cos{\theta} + (\vec{v} \cdot \vec{u})\vec{u}(1-\cos{\theta}) + \vec{u}\times\vec{v} \sin{\theta}\]

这时候虚部就是我们要得到的结果。而实部又正好为0,此时的四元数就像是一个向量转换过来的。不过这里我只是说明了四元数的旋转公式的正确性。至于其如何被发现的,我本身是没有找到原因的。

四元数和欧拉角间的转换

        在我们学习的时候,我们都听说过“欧拉角有万向节锁,所以我们要用四元数”的说法。但是各各游戏引擎都是让欧拉角和四元数共存的。当然这样做是有原因的,我们使用四元数来表示旋转对于大部分人而言太抽象,远不如欧拉角来得简单。所以游戏引擎才将这两个用法保留下来。既然这两种方法都存在,那势必要做一些处理来让这两个用法进行互相转换。

        欧拉角根据旋转顺序的不同,会有不同的表达。我们这里以ZXY的顺序来进行。而欧拉角为:\(angle=(\alpha,\beta,\gamma)\) ### 欧拉角转为四元数         欧拉角本身的意义就表示向量绕着对应的轴旋转一定的角度,也就是说我们可以看做是三个表达旋转的四元数拼凑在一起的结果。即: \[q_x = \cos{\frac{\alpha}{2}} + \sin{\frac{\alpha}{2}}i\] \[q_y = \cos{\frac{\beta}{2}} + \sin{\frac{\beta}{2}}j\] \[q_z = \cos{\frac{\gamma}{2}} + \sin{\frac{\gamma}{2}}k\]

其中\(q_x\)表示绕x轴旋转\(\alpha\)角的四元数(后面计算的时候,我们其实还要乘以他们的逆)。因为x旋转轴为(1,0,0),所以后面只有\(i\)保留了下来,其他也是同理。因为四元数的乘法不具有交换律,我们要严格按照角的旋转顺序进行乘。故最后的四元数表达如下:

\[q = q_z\cdot q_x\cdot q_y\]

我们并不需要再计算并存储\(q^{-1}\),因为\(q_x,q_y,q_z\)都是单位四元数,所以\(q^{-1}\)只需要将\(q\)虚部取反便可以得到。

四元数转欧拉角

        如果你仔细运算了上文提到的欧拉角转为四元数的式子,那么它的结果如下所示:

\[ \begin{aligned}q=&\cos{\frac{\alpha}{2}}\cos{\frac{\beta}{2}}\cos{\frac{\gamma}{2}} - \sin{\frac{\alpha}{2}}\sin{\frac{\beta}{2}}\sin{\frac{\gamma}{2}}\\=&+(\sin{\frac{\alpha}{2}}\cos{\frac{\beta}{2}}\cos{\frac{\gamma}{2}}+\cos{\frac{\alpha}{2}}\sin{\frac{\beta}{2}}\sin{\frac{\gamma}{2}} )i\\=&+(\cos{\frac{\alpha}{2}}\sin{\frac{\beta}{2}}\cos{\frac{\gamma}{2}} - \sin{\frac{\alpha}{2}}\cos{\frac{\beta}{2}}\sin{\frac{\gamma}{2}})j\\=&+(\cos{\frac{\alpha}{2}}\cos{\frac{\beta}{2}}\sin{\frac{\gamma}{2}} + \sin{\frac{\alpha}{2}}\sin{\frac{\beta}{2}}\cos{\frac{\gamma}{2}})k\end{aligned}\]

上面的式子确实有些复杂,我们不妨就设这个\(q\)\(q=w+xi+yj+zk\)。则欧拉角的计算公式如下: \[\alpha=\arctan(\frac{2(wz-xy)}{1-2(x^2+z^2)})\] \[\beta=\arctan(2(wx+yz))\] \[\gamma=\arctan(\frac{2(wy-xz)}{1-2(x^2+y^2)})\]

上面的式子因为时间原因我其实并没有真正验算出来,这是我在网上搜到的式子。所以这可能是错误。

总结

        幸好游戏引擎工程师都将这些复杂的运算都封装好了,因此我们少了很多的麻烦。但是我还是认为我们有必要去了解一下的。除了游戏本身的控制游戏内物体的逻辑外,我们还有渲染方面的需求。而有一些渲染的效果就需要我们对四元数本身有一定的了解。

        即使工程师们将这些运算封装的很好了,但是有时候因为一些原因而导致一些问题。比如我就遇到过四元数转欧拉角时出现精度误差导致的错误。最后我解决方案是直接全部使用四元数进行运算,这才避免了使用欧拉角时的精度误差。

碎碎念

        本身这篇文章的内容不多,但是文章很多地方需要使用Latex进行描写,再加上大量的公式推导。所以我写到后面真的有些不想写了,幸好我肚中的墨水不多(我到是希望多一点,这样我工作也能找个更好的(╥╯^╰╥))也就只能写到这里了。之后我再有学到四元数的知识再慢慢补就是了。