旋转的数学表达:欧拉角、轴向角、四元数与矩阵

  本文发布于游戏程序员刘宇的我的博客,长期更新,转载请注明源地址https://www.cnblogs.com/xiaohutu/p/10979936.htmlhtml

  数学,是人类对客观世界中数量关系和空间形式本质特征进行研究的科学。对一样的某一特征或者关系,能够根据需求用不一样的数学符号、定义和过程来表达。在游戏引擎中,咱们也有不少这样的例子,好比本文说到的旋转。程序员

欧拉角

  旋转是一个过程,一个物体围绕周或者点角度变化的过程。为了描述这个过程咱们必须有参照物,因而咱们先定义一个世界坐标系,笛卡尔坐标系。spa

      

  欧拉角用(x, y, z) 分别来表示这个物体相对三个坐标系的夹角,这是由数学家欧拉首先提出而得名的。 .net

          

  然而仅仅有(x, y, z) 来表示旋转是不够的,还有两个因素:3d


  首先是旋转顺序,从各个轴上进行角度旋转时xyz前后的不一样会获得不一样的结果。咱们称这个顺序定义为顺规,下面一段是维基百科的定义:
  在经典力学里,时经常使用zxz顺规来设定欧拉角;照着第二个转动轴的轴名,简称为x顺规。另外,还有别的欧拉角组。合法的欧拉角组中,惟一的限制是,任何两个连续的旋转,必须绕着不一样的转动轴旋转。所以,一共有12种顺规。例如,y顺规,第二个转动轴是y-轴,时经常使用在量子力学、核子物理学、粒子物理学。另外,还有一种顺规,xyz顺规,是用在航空航天工程学;
  按(z-x-z, x-y-x, y-z-y, z-y-z, x-z-x, y-x-y)轴序列旋转,即第一个旋转轴和最后一个旋转轴相同,咱们称之为经典欧拉角(Proper Euler Angle)
  按(x-y-z, y-z-x, z-x-y, x-z-y, z-y-x, y-x-z)轴序列旋转,即三个不一样的轴,咱们称之为泰特布莱恩角(Tait–Bryan angles)code


  其次是旋转的参照坐标系,欧拉角按旋转的坐标系分为:
  内旋(intrinsic rotation)即按照物体自己的坐标系进行旋转,坐标系会跟随旋转与世界坐标系产生偏移。
  外旋(extrinsic rotation)即根据世界坐标系进行旋转。
  这是咱们看看Unity3d中Transform的Rotate,最后一个参数即坐标系:  orm

public void Rotate(Vector3 eulerAngles, Space relativeTo = Space.Self);

   注意:Unity3d使用的是zxy的顺规,且进行一次欧拉旋转的zxy依次执行过程当中,相对轴始终是运算开始以前的轴向。xml

 万向节死锁(Gimbal Lock)

  注意咱们前面提到的旋转的规则 [ 进行一次欧拉旋转的zxy依次执行过程当中,相对轴始终是运算开始以前的轴向 ]htm

  在这个规则下,zxy顺规时,若是x的旋转为90度,那么任意的(z,90,y)都与(z-y,90,0)获得的结果都是相同的,此时咱们称z轴失去了自由度,并称这种状况为万向节死锁。之因此叫Gimbal Lock,是由于这种状况正好和Gimbal,即陀螺仪的状况是完美匹配的,死锁状况也相同。具体状况这篇文章写的很好,你们能够看一下:https://blog.csdn.net/andrewfan/article/details/60981437blog

             

  这种状况下,物体的某一个旋转,会有多个欧拉角数值与其多对一的对应,那么对欧拉角进行插值是没有意义的。若是只旋转一个轴,其余轴不动,那么直接设置欧拉角的数值却是没有什么问题。

  同时咱们也不可贵出死锁的缘由并非由顺规决定的,而是因为欧拉角的原理和计算方式,一个轴的旋转必然带来另外轴的旋转所致使的。

 轴向角

  这时候,又有了一个思路,咱们可使用基于一个单位矢量来表示方向,再用一个标量来定义绕这个矢量来旋转多少角度theta。

  即[x,y,z,theta],前面的xyz表示这个矢量,最后一个表示角度。这个表示方法很简洁明了,容易理解。

      

  缺点有两个:

  1.不一样的轴角之间不能进行简单的插值。

  2.很差基于一个矢量加上一个轴角形式的旋转来进行运算。

  为了解决这些缺点,先人们又发明了使用四元数和矩阵来表达旋转。

四元数

  这里先说一下基本原理:

  参考连接:https://www.3dgep.com/understanding-quaternions/

  译文:https://blog.csdn.net/lhs322/article/details/80066960

  四元数的概念是由爱尔兰数学家汉密尔顿发明的,他当时正和老婆一块儿前往爱尔兰皇家研究院,一边走一边想,路过一座桥时,他顿悟了公式,并马上把它刻在桥上的石头上:

     

   那么,为何四元数能表示三维空间的旋转呢?首先学太高数咱们都知道复数的定义以及几何意义,复数能够映射到复数平面上,而且对这复数乘以i,获得的复数就至关于复数空间里旋转了90度。

   例以下图,p = 2 + i,乘以i后: q = pi = (2+i)*i = 2i + i*i = 2i - 1 = -1 + 2i。能够看出q逆时针旋转了90度。同理乘以-i即为正时针旋转90度。

      

  此时将复数的虚部扩展为三个,并根据汉密尔顿的著名表达式以及推论

   

  

  四元数的定义能够用来表达笛卡尔坐标系的旋转,其中i,j,k分别表明笛卡尔坐标系里xyz三个轴的单位向量。这些表达式里 ij = k 是否是很眼熟?两个互相垂直的单位向量的叉乘等于垂直于两个向量的单位向量。

   

   通过一系列的推导和运算(略),你们感兴趣能够看上面的连接,假设一个旋转的基准向量是(A,B,C),角度是 θ (theta),那么表达这个旋转过程的四元数以下:  

  

  注意ABC本质上就是基准向量在3个笛卡尔轴上的份量,用来准确描述向量,上面的公式也就是

四元数 q = [cos(θ/2), sin(θ/2)* v‘]; 
即:
w = cos(θ/2) x = A * sin(θ/2) y = B * sin(θ/2) z = C * sin(θ/2)

  假设有一个向量v要进行旋转,这个旋转描述为q,那么结果是 v' = qvq-1, 若是要进行屡次旋转,则表示为:

  

  四元数的乘法是:

q1 * q2 =
(w1*w2 - x1*x2 - y1*y2 - z1*z2) + (w1*x2 + x1*w2 + y1*z2 - z1*y2) i + (w1*y2 - x1*z2 + y1*w2 + z1*x2) j + (w1*z2 + x1*y2 - y1*x2 + z1*w2) k

  能够看到,经过一系列的数学推导和定义,能够只用4个浮点数就来表达一个旋转过程,而且能够方便简单的快速计算旋转的叠加。这对于游戏引擎来讲是很是有意义的,能够加快运算速度。

  四元数还有不少具体的特性,计算规则等,感兴趣的能够去研究,本文主要讨论旋转,这里再也不赘诉。  

  【球形插值】

  四元数还能够实现球形插值,制定两个旋转qa到qb,时间间隔为t,那么此刻的旋转插值为:

  

  其中θ为两个旋转之间的夹角:

  

  球面插值能够在游戏里实现很平滑的转向和球面运动。

四元数与欧拉角之间的转换

  已知欧拉角,求四元数:

  

  已知四元素,求欧拉角

  

用矩阵来计算旋转

   学过矩阵乘法咱们都知道,若是把向量当作一个列矩阵,那么与向量维度相同的列数的矩阵乘以它,获得的结果也是一个列矩阵,即:

  

  因此能够充分利用左边矩阵的内容,对右边的向量进行各类变换(包括平移,缩放,旋转等等),这里咱们只讨论旋转。

  具体推导过程参考这里:https://www.cnblogs.com/xpvincent/archive/2013/02/15/2912836.html

  假设一个向量V(Vx,Vy, Vz) 绕另一个轴角(nx, ny, nz, θ)进行旋转,那么旋转结果V'是:

  

  这个公式咱们称之为罗德里格旋转公式(Rodrigues' rotation formula),用矩阵计算旋转在游戏图形渲染里很是广泛,不过引擎里通常为了兼容更多的向量变换,都使用了其次矩阵,即多一个维度的矩阵,本文不表。

 小结

  总结完这几种在常见的表达旋转的数学方式,能够看到游戏引擎里也都使用到这些表达方法,感受收益匪浅,后面准备讨论一下齐次矩阵和矩阵变换的原理。

相关文章
相关标签/搜索