ELBE
电话:021-58780503 微信:18017854633 邮箱:sales_fullyear@126.com 地址:上海市浦东新区城南路168弄3号峰汇商务广场B座911室四元数应用——转矩阵、Slerp插值与万向节
四元数应用——顺序无关的旋转混合
—————————————————————————————————————————
今天说一些关于四元数的相关实例,较前两篇可能有些零碎,算是对于前两章的补充说明。具体来说,我们接下来会讨论三个问题,第一个是四元数与矩阵的转换,其次是四元数的插值问题,最后说一下万向节死锁问题。话不多说,开始正题。
1.四元数的矩阵形式目前对于大多数底层的图形API,其对于空间坐标的转换还是基于矩阵。所以当我们使用四元数对旋转进行操作,最终传递给顶点着色器的数据还是要以矩阵的形式。不过,从应用层的角度来说,我们只要满足如下的公式就可以实现转换。
我相信大多数人并不满足于背公式,推导该公式的方法主要有两种。其中一种就是从代数的角度进行求解,说白了就是硬算。我们在《四元数和旋转(二)》中提到一个公式:
我们分别求出各项,可以得到三个矩阵,然后相加就是上面那个转换公式。这里不做推导,有兴趣的可以自己算一算。下面介绍一种比较灵性的一种证明方法。当给定下面两个四元数:
我们令他们相乘,其实可以写成矩阵与向量相乘的形式,如下图:
可以这么说,两个四元数相乘转变成矩阵L右乘四元数q。发散一下思维,我们还可以将该行为写成矩阵R左乘四元数q,具体形式如下图:
最体看L和R括号里面的四元数下标,由于四元数不满足交换律,所以顺序很重要,而括号里面的下标其实是不一样的,这个地方一定要注意。基础知识讲完了,下面就直接进行应用。根据旋转公式和以上的定义,我们很容易得到如下公式:
然后将L和R带入上述的矩阵中,我们可以得到:
证毕!
2.四元数的Slerp插值其实插值问题一直都算是图形学中比较经典的问题,一般来说线性插值可以满足大多数的情况,但是对于旋转来说,线性插值肯定效果不好,我们来看下图:
从图中很明显看出,左图(线插)要逊于右图(球插)。一种从动画的角度解释是,为了保证每帧的旋转都是均匀变化的,线性插值得到的旋转结果一定是不均匀的,这主要考虑的是旋转角度。那我们从代数的角度去思考,如果两个单位四元数之间进行插值,如左图的线性插值,得到的四元数一定不是单位四元数,我们期望对于旋转的插值应该是不改变长度的,所以显然右图球面(Slerp)插值更为合理。
关于四元数的球面插值证明就很多了,Wiki上面就有很详细的证明以及实现的Code。主体思想其实就是施密特正交,先根据 和 解算出两个正 交的四元数,然后通过加权算出最终的 。下图能很好的说明白这个事。
下面主要讨论的是这个问题,上图中给出了球面插值公式的一种形式,我们暂且称为加法形式。由于四元数旋转是相乘的形式,我们上述的公式亦可以写成:
鉴于刨根问底的精神,首先先说说四元数的差(Difference),这个比较好理解,类似于矩阵,A和B的差,可以理解成先旋转A逆然后再旋转B,得到A和B之间的差值,表示为 。然后我们如果去t倍的差或者直接说差乘以一个t因子,这里就要引用了我们在《四元数与旋转(一)》中所述的四元数指数形式。对于 ,我们来看看 和 的情况,具体如下:
最后我们令 以及 ,根据下面公式重新审视一下球面插值:
可以看出,两种表现形式之间是可以互相转化的。
3.万向节死锁其实这个问题不能算的上四元数的应用,万向节死锁早期是用来处理机械臂在旋转时自由度缺失的问题,由于当时机械臂的关节都是单自由度的,所以在模拟人体某些球形关节(自由度为3的关节)的时候,会采用三组相互正交的机械关节进行模拟。
如图所示,机械关节1-3共同模拟了手腕的活动,这里会导致一个问题,当关节2旋转90°后,关节1和关节3会重合,这样无论旋转关节1还是旋转关节3都只会沿着 轴进行旋转,这就是万向节死锁。值得一提的是,当你旋转关节1,关节2和关节3都会随着一起动,而当你旋转关节3,无论怎么旋转也不会影响关节2和关节1,所以只有关节2旋转90°会产生万向节死锁。
当早期计算机动画将机器人那套东西搬过来的时候,最简单的做法就是用欧拉角直接模拟旋转。这就导致了万向节死锁。那怎么去理解计算机动画里面的这个问题呢,毕竟动画里面也没有机械关节。
我们假定三个欧拉角的旋转顺序如下
当 的时候, 和 就会重合,如右图,这样就会产生万向节死锁。
所以为了避免这个问题,图形学旋转开始使用轴角,因为轴角可以讲三个欧拉角等效成一个绕着特定轴旋转的角度。低版本的OpenGL有个函数glRotate函数,实现的方法就是使用轴角。当然,并不是轴角不存在万向节死锁,当存在三个相互正交的轴角按一定顺序进行旋转,依然会产生万向节死锁,不过这种情况很难发生。
那么轴角已经解决这个问题,为什么我们要用四元数?
4.四元数总结最后点个题,呼应一下前文,具体说说四元数的好处:
解决万向节死锁(Gimbal Lock)问题。(不要去用四元数模拟欧拉角!)仅需存储4个浮点数,相比矩阵更加轻量。比如矩阵至少要用9个float进行表示旋转信息,即便加上旋转缩放,也会比矩阵少存2-6个float,对于在PC上开发的游戏可能并不显著,毕竟现在内存都大,但在家用机(特别是上世代)上,内存比较吃紧的情况下就相当有利了。四元数无论是求逆、串联等操作,相比矩阵更加高效。比如取反就等同于求逆,即便是正交矩阵,转置的操作代价也比取反要高得多,更何况大多数情况由于存在缩放,求逆的操作会更加复杂。但是,使用四元数需要考虑将四元数与矩阵之间转换的成本,不过综合考虑,还是四元数操作成本比较低。基本上四元数在我这也就告一段落了,之后会有请人 @Obsver Anonym 续一篇具体应用的文章,请大家敬请期待!