一、了解四元数
在本文中,我将尝试以一种易于理解的方式来解释四元数的概念。我将说明您如何可视化四元数,并说明可应用于四元数的不同操作。我还将比较矩阵,欧拉角和四元数的应用,并尝试解释何时要使用四元数代替欧拉角或矩阵,何时不使用。
内容
在计算机图形学中,我们使用变换矩阵来表示空间中的位置(平移)及其在空间中的方向(旋转)。可选地,单个变换矩阵也可以用于表示对象的比例或“剪切”。我们可以将转换矩阵视为“基本空间”,如果您将向量或点(甚至另一个矩阵)乘以转换矩阵,则可以将该向量,点或矩阵“转换”为该矩阵表示的空间。
在本文中,我将不讨论转换矩阵的详细信息。有关转换矩阵的详细说明,您可以参考我以前的文章《矩阵》。
在本文中,我想讨论一种使用四元数描述空间中物体(旋转)方向的替代方法。
四元数的概念由爱尔兰数学家William Rowan Hamilton爵士于1843年10月16日星期一在爱尔兰都柏林实现。汉密尔顿和他的妻子一起去爱尔兰皇家艺术学院的途中,当他经过布劳姆大桥上的皇家运河时,他突然意识到他立刻在桥的石头上雕刻了出来。
威廉·罗恩·汉密尔顿(William Rowan Hamilton)位于皇家运河布鲁姆桥上的纪念匾,以纪念他发现四元数乘法的基本公式。
复数在我们完全理解四元之前,我们必须首先了解它们的来源。四元数的根基于复数系统的概念。
除了众所周知的数字集(Natural,Integer,Real和Rational),复数系统还引入了一组新的数字,称为虚数。发明了虚数来求解某些无解的方程,例如:
为了解决该表达式,我们必须声明,我们知道这是不可能的,因为任何数字(正数或负数)的平方始终为正。
数学家通常不能接受表达式没有解,因此发明了一个新术语,称为虚数,可以用来解这些方程。
虚数的形式为:
不要试图真正理解这个术语,因为没有逻辑上的理由说明它存在的原因。我们只需要接受 i 的平方等于-1.
虚数集可以用表示。
复数集(由符号C表示)是实数和虚数的总和,其形式为:
也可以说所有实数都是b=0的复数,所有虚数都是复数a=0。
复数加减法
可以通过增加或减少实部和虚部来增加和减少复数。
加法:
减法:
复数乘以标量
通过将复数的每一项乘以标量,将复数乘以标量:
复数乘积
复数也可以通过应用常规代数规则相乘。
复数平方
复数也可以通过乘以平方来求平方:
共轭复数
共轭复数是否定的虚部的复数,并表示为或
。
复数与其共轭的乘积给出了特殊的结果。
复数的绝对值
我们可以使用复数的共轭来计算复数的绝对值(或范数或大小)。复数的绝对值是复数乘以其共轭的平方根,并表示为|z|:
两个复数的商
为了计算两个复数的商,我们将分子和分母乘以分母的复共轭。
i 的幂
如果我们说那么也应该有可能将提升 i 到其他幂。
如果我们继续写这个序列,我们会看到一个模式出现
在幂指数负增长的时候一个相似的情况出现了。
您之前可能已经在数学上看到过类似的模式,但是形式为(x,y,−x,−y,x,…),它是通过在2D笛卡尔平面上逆时针旋转点90°而产生的(x,−y,−x,y,x,…)是通过在2D笛卡尔平面上顺时针旋转点90°生成的。
笛卡尔平面
复杂平面我们还可以通过在水平轴上映射实数部分在垂直轴上映射虚数部分,在称为复杂平面的2D网格中映射复数。
复杂平面
如前面的序列所示,可以说,如果将复数乘以 i,则可以将复数以90°的增量旋转通过复数平面。
让我们看看这是不是真的。我们将在复平面上取任意点p:
我们让 p 乘以i 得到q:
q 乘以 i 赋值给 r:
r 乘以 i 赋值给 s:
s 乘以 i 赋值给 t:
正是我们从(p)开始。如果将这些复数绘制在复数平面上,则会得到以下结果。
![了解四元数之美_游戏引擎开发_32](http://dev-img.mos.moduyun.com/20231112/822e902d-5c69-43d8-9f06-bebb06fb72fb.png)
复平面上的复数
我们也可以通过将复数乘以-i 在复平面中顺时针旋转。
转子
我们还可以通过定义以下形式的复数来在复杂平面中执行任意旋转:
将任何复数乘以转子q 即可得出以下通式:
也可以用矩阵形式编写:
这是使复平面中的任意点绕原点逆时针旋转的方法。
四元数有了复数系统和复平面的知识,我们可以通过在 i 之外向我们的数系统添加两个虚数,将其扩展到3维空间。
表达四元数的一般形式是
根据汉密尔顿的著名表达:
和
您可能已经注意到 i ,j 和k 之间的关系与单位笛卡尔向量的叉积规则非常相似:
汉密尔顿还认识到虚数 i , j 和 k 可以用来表示三个笛卡尔单位向量 i ,j 和 k 具有相同的虚数属性,因此
可视化属性 ij,jk,ki
上图显示了由 i , j 和 k 表示的笛卡尔单位向量之间的关系。
四元数作为有序对
我们还可以将四元数表示为有序对:
其中 v 也可以由其各个组成部分表示:
使用此表示法,我们可以更轻松地显示四元数和复数之间的相似性。
四元数的加减
四元数可以和复数一样进行相加或相减:
四元数乘法
我们还可以表示两个四元数的乘积:
从而得到另一个四元数。如果我们用有序对(也称为四元数单位)替换前面表达式中的虚数i、j和k,
把这个表达式展开成有序对的和,得到:
如果我们与四元数单位相乘并提取公共向量分量,我们可以这样重写此方程:
这个方程给出了两个有序对的和。第一个有序对是实四元数,第二个是纯四元数。这两个有序对可以组合成一个有序对:
如果我们替换,
我们得到:
这是四元数积的一般方程。
实四元数
实四元数是向量项为0的四元数
两个实四元数的乘积是另一个实四元数:
它类似于包含零虚项的两个复数的乘积。
四元数乘以标量
我们也可以将四元数乘以一个标量,这个标量应该遵守以下规则:
我们可以使用上面所示的乘积或实四元数将四元数乘以标量作为实四元数来确认这一点:
纯四元数
类似于实四元数,汉密尔顿还将纯四元数定义为标量项为零的四元数:
或者,写在其组成部分中:
我们也可以取两个纯四元数的乘积:
根据上面所示的四元数积规则。
四元数的加法形式
我们还可以将四元数表示为实四元数和纯四元数的一部分:
单位四元数
给定一个任意向量v,我们可以用它的标量大小和方向来表示这个向量:
把这个定义和纯四元数的定义结合起来,可以得到:
我们还可以描述一个具有零标量和单位向量的单位四元数:
四元数的二元形式
我们现在可以将单位四元数的定义和四元数的加法形式结合起来,我们可以创建一个四元数的表示法,它类似于用于描述复数的符号:
这为我们提供了一种表示与复数非常相似的四元数的方法:
共轭四元数
可通过对四元数的向量部分求反来计算四元数共轭:
四元数与其共轭的乘积给出:
四元数范数
如果您从复数范数的定义中回想起:
类似地,四元数的范数(或大小)定义为:
它允许我们将四元数的范数表示为:
四元数归一化
根据四元数范数的定义,我们可以用它来规范化四元数。一个四元数被| q |整除:
例如,让我们规范化四元数:
首先,我们必须计算四元数的范数:
然后,我们必须将四元数除以四元数的范数,以计算归一化的四元数:
四元数的逆
四元数的逆表示为q-1。为了计算四元数的逆,我们取四元数的共轭并除以范数的平方:
为了说明这一点,我们可以认为,根据四元数的逆的定义:
并将两边乘以四元数的共轭得到:
通过替换,我们得到:
对于范数为1的单位范数四元数,我们可以这样写:
四元数点积
与向量点积相似,我们也可以通过乘以相应的标量部分并将结果相加来计算两个四元数之间的点积:
我们还可以使用四元数点积来计算四元数之间的角度差:
对于单位范数四元数,我们可以简化等式:
旋转
如果您还记得,我们定义了复数的一种特殊形式,称为转子,它可用于通过二维复平面旋转点,如下所示:
然后,由于其与复数的相似性,应该有可能这样表达一个四元数,该四元数可用于旋转3D空间中的点:
让我们通过计算四元数q和向量p的乘积来检验这个理论是否成立。首先,我们可以用以下形式将p表示为纯四元数:
q是单位范数四元数的形式:
然后,
我们看到结果是一个具有标量部分和向量部分的通用四元数。
让我们首先考虑“特殊”情况,其中p垂直于在这种情况下,点积项
,结果变成纯四元数:
在这种情况下,为了使p绕旋转,我们只需将
和
代入。
例如,让我们绕z轴旋转一个向量p 45°然后我们的四元数q是:
让我们取一个向量p,它依附于p垂直于k的特殊情况:
现在让我们计算qp的乘积:
从而得到一个纯四元数,它绕k轴旋转45°。我们还可以确认,结果向量的大小保持不变:
这正是我们所期望的结果!
我们可以通过下面的图片将其可视化:
四元数旋转(1)
现在让我们考虑一个与p不正交的四元数。如果我们将四元数的向量部分指定为距p 45°的偏移,我们得到:
把向量p乘以q,我们得到:
代入和
得出:
它不再是一个纯四元数,也没有旋转45度,向量的范数也不再是2(相反,它已经减少到).
这个结果可以通过图像可视化。
四元数旋转(2)
从技术上讲,在三维空间中表示四元数p′是不正确的,因为它实际上是一个4D向量!为了简单起见,我将只可视化四元数的矢量分量。
然而,一切都没有失去。汉密尔顿认识到(但没有发表)如果我们把qp的结果乘以q的逆
然后得到一个纯四元数,并保持向量分量的范数。让我们看看能否把这个应用到我们的例子中。
首先,让我们计算q-1:
把代入得:
并结合qp和q-1的先前值给出:
这是一个纯四元数,结果的范数是:
与p相同
所以向量的范数保持不变。
下图显示了旋转的结果。
四元数旋转(3)
所以我们可以看到结果是一个纯四元数,并且初始向量的范数保持不变,但是向量旋转了90度而不是45度,这是期望值的两倍!因此,为了正确地将向量p绕任意轴v^旋转一个角θ,我们必须考虑半角并构造以下四元数:
这是旋转四元数的一般形式!
四元数插值在计算机图形学中使用四元数的最重要原因之一是四元数非常善于表示空间旋转。四元数克服了困扰3D空间中其他旋转点方法的问题,例如Gimbal锁定,当您用欧拉角表示旋转时会遇到此问题。
使用四元数,我们可以定义几种表示3D空间中旋转插值的方法。我将研究的第一种方法称为SLERP,该方法用于在两个方向之间平滑地插入点。第二种方法是SLERP的扩展,称为SQAD,用于对定义路径的一系列定向进行插值。
SLERP
SLERP代表球面线性插值。SLERP提供了一种平滑地插值两个方向上的点的方法。
我将第一个方向表示为q1,第二个方向表示为q2。插值点用p表示,插值点用p′表示。当t=0时,插值参数t将从q1插值到t=1时的q2。
标准线性插值公式为:
应用此公式的一般步骤是:
- 计算p1和p2之间的差
- 把差额的小数部分计算出来。
- 通过两点之间的分数差调整原始值。
我们可以使用相同的基本原理在两个四元数方向之间进行插值。
四元数差分
第一步要求我们必须计算 q1 和 q2 之间的差。关于四元数,这等效于计算两个四元数之间的角度差。
四元数指数
下一步是把差额的小数部分去掉。我们可以将四元数的小数部分提升到一个值在[0…1]范围内的幂。
四元数指数化的一般公式是:
其中四元数的指数函数由下式给出:
四元数的对数由下式给出:
当t=0时,我们有:
当t=1时,我们有:
四元数的分数差分
为了计算插值的角旋转,我们用q1和q2之间的差的小数部分来调整原始方向q1。
这是四元数球面线性插值的一般形式。然而,这并不是实际中常用的SLERP方程的形式。
我们可以应用一个类似的公式来执行向量到四元数的球面插值。向量球面插值的一般形式定义为:
这在下图中是可视化的。
四元数插值
此公式可以未经修改应用于四元数:
通过计算q1和q2之间的点积可以得到角θ。
注意事项
执行过程中必须考虑到两个问题。
首先,如果四元数点积的结果是负值,那么得到的插值将绕着4D球体“长途跋涉”,这不一定是我们想要的。为了解决这个问题,我们可以测试点积的结果,如果它是负的,那么我们可以否定其中一个方向。对四元数的标量和矢量部分求反不会改变它所表示的方向,但这样做可以保证旋转将应用于“最短”路径。
另一个问题是当q1和q2之间的角差很小时,sinθ变为0。如果发生这种情况,那么我们除以sinθ就会得到一个未定义的结果。在这种情况下,我们可以回到使用q1和q2之间的线性插值。
SQUAD
就像SLERP可以用来计算两个四元数之间的插值一样,SQUAD(球面和四边形)也可以用来在旋转路径上平滑地进行插值。
如果我们有四元数序列:
我们还定义了“helper”四元数(si),我们可以考虑一个中间控制点:
然后沿子曲线的方向定义为:
时间t由以下给出:
结论
尽管很难理解,四元数比使用矩阵或欧拉角来表示旋转提供了一些明显的优势。
- 利用SLERP和SQUAD进行四元数插值,为空间方向间的平滑插值提供了一种方法。
- 使用四元数的旋转连接比组合以矩阵形式表示的旋转更快。
- 对于单位范数四元数,则通过减去四元数的矢量部分来获得旋转的逆。如果矩阵不是正交的,则计算旋转矩阵的逆要慢得多(如果是,则只是矩阵的转置)。
- 将四元数转换为矩阵的速度略快于欧拉角。
- 四元数只需要4个数字(如果它们是标准化的,则需要3个数字)。实际部分可以在运行时计算)以表示矩阵至少需要9个值的旋转。
然而,尽管有利于使用四元数的所有优点,也有一些缺点。
- 由于浮点舍入误差,四元数会变得无效,但是可以通过重新标准化四元数来解决此“错误蠕变”问题。
- 使用四元数的最重要的威慑因素可能是它们很难理解。我希望阅读本文后能解决此问题。
有几个数学库可以实现四元数,而其中一些库可以正确实现四元数。根据我的个人经验,我发现GLM(OpenGL数学库)是一个很好的数学库,具有很好的四元数实现。如果您对在自己的应用程序中使用四元数感兴趣,这是我推荐的库。
下载演示我创建了一个小型演示,演示了如何使用四元数旋转空间中的对象。该演示是使用Unity 3.5.2 创建的,您可以免费下载并查看演示脚本文件。该zip文件还包含Windows二进制可执行文件,但是使用Unity时,您还可以生成Mac应用程序(Unity 4也引入了Linux版本)。
参考