【Unity编程】四元数(Quaternion)与欧拉角

欧拉旋转、四元数、矩阵旋转之间的差异

除了欧拉旋转以外,还有两种表示旋转的方式:矩阵旋转和四元数旋转。接下来我们比较它们的优缺点。


欧拉角

四元数

内部由四个数字(在Unity中称为x,y,z和w)组成,然而这些数字不表示角度或轴,并且通常不需要直接访问它们。除非你特别有兴趣深入了解四元数学,你只需要知道四元数表示三维空间中的旋转,你通常不需要知道或修改x,y和z属性。

  • 优点:四元旋转不存在万向节锁问题。
  • 优点:存储空间小,计算效率高。
  • 弱点:单个四元数不能表示在任何方向上超过180度的旋转。
  • 弱点:四元数的数字表示不直观。

矩阵旋转

  • 优点:与四元数一样,不存在万向节锁问题
  • 优点:可以表示围绕任意轴的旋转,四元数的旋转轴均为通过物体中心点的轴,矩阵则不受限
  • 缺点:矩阵旋转使用4x4矩阵,记录16个数值,而四元数只需要4个数值。计算复杂,效率低。

由于Unity中的旋转使用四元数,因此本文重点就放在四元数数学上。矩阵还是某些场合需要用到,比如坐标变换。不过你几乎不需要手动去执行矩阵计算,除非你在做Shader编程,或者是某些需要极端提高效率的场合。后续我计划写一篇文章专门介绍矩阵变换。

四元数的数学

由于前面两篇文章均尽可能采用无数字计算的方式,为的是方便理解。而这里由于需要理解四元数的计算,我们还是需要理解一些复数和欧拉旋转计算方面的基本数学。

复数

首先了解一下复数,上图中,左右方向的轴(X轴)称为实数轴,上下的轴(Y轴)称为虚数轴。

任意一个二维矢量,都可以使用一个复数形式进行表示。也就是:e = a + bi

其中a是实数,i是虚数,i * i=-1

至于什么是虚数,我的理解它就是个不同维度的后缀标记和符号而已。i标记了实数轴和虚数轴之间的差异,也就是旋转90度旋转的差别,乘以i意味着这种旋转关系。

为什么i * i=-1?也就是两个虚数i乘积又变成了实数-1?因为从实数轴(1表示)围绕上图中逆时针90度(1*i)到达虚数轴(=i),再逆时针旋转90(1 * i * i)就到达了负实数轴方向(=-1)。所以这里规定i * i=-1。

复数运算

  • 加法: (a+bi)+(c+di)=(a+c)+(b+d)i
  • 减法: (a+bi)-(c+di)=(a-c)+(b-d)i
  • 乘法: (a+bi)(c+di)=ac+bci+adi+bdi^{2}=(ac-bd)+(bc+ad)i

更多复数相关知识,请参考“维基百科-复数_(数学)”

欧拉旋转定理

为了方便讨论旋转,我们避开矢量长度的影响,也就是假设问题是基于长度为1的矢量去讨论。由上图可以看出,当长度为1时,矢量落在长度为1的圆形上,此时实数轴上的a = cos(φ),虚数轴上的b = sin(φ),其中φ为旋转角度。

此时的表示形式为 e = cos(φ) + sin(φ)i

那么,使用这样一种形式到底有什么意义呢?数学从来都是工具,如果没有用处,它也就不会存在了。我们接下来看它的作用。

当圆上的一个矢量进行了连续的旋转时时,假设先旋转φ,再旋转θ,则结果应该是两个旋转的角的和的复数形式,即 e = cos(φ+θ)+sin(φ+θ)i

假设:

e1= cos(φ) + sin(φ)i (表示旋转了φ角度)

e2= cos(θ) + sin(θ)i (表示旋转了θ角度)

那么e和e1、e2之间的关系是怎样的?



根据和差公式可以得知

cos(φ+θ)+sin(φ+θ)i

= (cos(φ)cos(θ)-sin(φ)sin(θ))+(sin(θ)cos(φ)+cos(θ)sin(φ))i

= cos(φ)cos(θ)+cos(φ)sin(θ)i+cos(θ)sin(φ)i- sin(φ)sin(θ)

= (cos(φ) + sin(φ)i)*(cos(θ) + sin(θ)i)

= e1 * e2

=>

e = e1*e2



也就是说,连续的旋转(例如这里旋转φ再旋转θ),可以使用两个复数的乘积进行表示。在计算机运算中:

  • 如果知道φ=30度,那么计算此单位矢量再旋转θ=40度是很容易的,只需要直接运算cos(φ+θ)和sin(φ+θ)即可。
  • 如果知道一个矢量(a+bi),不知其φ值,要对其进行旋转θ=40度,那么也就有了快速的计算方法,即:

    (a+bi) * (cos(40)+sin(40)i) = (a * cos(40)-b * sin(40)) + (b * cos(40) + a * sin(40))i

    结果:二维向量 x = a * cos(40)-b * sin(40),y= b * cos(40) + a * sin(40)

    可以快速计算出二维矢量结果,而不必先求φ。

因此这就是复数和欧拉旋转定理的作用,它可以使用复数很方便地表示出二维矢量的旋转变化。

四元数

相对于复数为二维空间,为了解决三维空间的旋转变化问题,爱尔兰数学家威廉·卢云·哈密顿把复数进行了推广,也就是四元数。

以下均为定义,所谓定义,就是我们人为设置的概念和计算方法,它们本身或许没有什么意义,但是如果按照这些概念和方法计算出某些有意义的结果,那么这些定义也就有了相应的意义。


四元数定义

四元数定义i、j、k三个虚数单位参与运算,并有以下运算规则:

j?k=i,k?j=?i;

j?k=i,k?j=?i;

k?i=j,i?k=?j;

i?i=j?j=k?k=i?j?k=?1

i、j、k仍然理解为旋转,其中:

  • i旋转代表X轴与Y轴相交平面中X轴正向向Y轴正向的旋转
  • j旋转代表Z轴与X轴相交平面中Z轴正向向X轴正向的旋转
  • k旋转代表Y轴与Z轴相交平面中Y轴正向向Z轴正向的旋转
  • -i、-j、-k分别代表i、j、k旋转的反向旋转

一个普通四元数可以写成如下形式:

qˉ=a+bi+cj+dk

四元数的i、j、k之间乘法的性质与向量之间的叉积结果形式很类似,于是四元数有了另外一种表示形式:

qˉ=((x,y,z)→,w)=(u? ,w)

加法定义

四元数加法,跟复数、矢量和矩阵一样,两个四元数之和需要将不同的元素加起来,加法遵循实数和复数的所有交换律和结合律:

qˉ1+qˉ2=w1+w2+u1→+u2→=(w1+w2)+(x1+x2)i+(y1+y2)j+(z1+z2)k

格拉斯曼积定义

四元数的乘法有很多种,最常见的一种定义,与数学多项式乘法相同,称为格拉斯曼积。(注意,下面乘积的式子是由多项式形如a+bi+cj+dk的多项式进行多对多乘法(比如4项x4项=16项)计算后,使用矢量的点积和叉积替代部分计算项后形成):

qˉ1qˉ2=w1w2?u1→?u2→+w1u2→+w2u1→+u1→×u2→

把向量部分和实数部分分开,可以写成:

qˉ1qˉ2=((w1u2→+w2u1→+u1→×u2→),(w1w2?u1→?u2→))

注意,格拉斯曼积符合结合率,也就是qˉ1qˉ2qˉ3=(qˉ1qˉ2)qˉ3=qˉ1(qˉ2qˉ3),但不符合交换律,一般来说,qˉ1qˉ2≠qˉ2qˉ1。

点积定义

点积也叫做欧几里得内积,四元数的点积等同于一个四维矢量的点积。点积的值是qˉ1中每个元素的数值与 qˉ2 中相应元素的数值的一对一乘积(比如4项x4项=4项)的和。这是四元数之间的可换积,并返回一个标量。

qˉ1?qˉ2=w1w2+u1→?u2→=w1w2+x1x2+y1y2+z1z2

叉积定义

四元数叉积:p × q

四元数叉积也称为奇积。它和矢量叉积等价,并且只返回一个矢量值:

pˉ×qˉ=pˉqˉ?qˉpˉ2

pˉ×qˉ=u? ×v?

pˉ×qˉ=(cz?dy)i+(dx?bz)j+(by?xc)k

共轭定义

四元数的共轭的定义。即实部相同,虚部相反。(与复数概念类似)

qˉ?=((?x,?y,?z)→,w)=(?v? ,w)

定义的推论

从以上三个定义,可以得出一个推论,一个四元数与其共轭的格拉斯曼积等于此四元数与其自身的点积:

qˉqˉ?=qˉ?qˉ=qˉ?qˉ=x2+y2+z2+w2

证明:
由格拉丝曼积定义可知

qˉ1qˉ2=w1w2?u1→?u2→+w1u2→+w2u1→+u1→×u2→

而此时,

qˉ1=qˉ,qˉ2=qˉ?

也就是,

u1→=u? ,u2→=?u? ,w2=w1=w

代入

qˉ1qˉ2=w1w2?u1→?u2→+w1u2→+w2u1→+u1→×u2→

得到

qˉqˉ?=ww+u? ?u? ?wu? +wu? +u? ×u?

由于第三四项可以消去,第五项本身为0向量,所以得出

qˉqˉ?=ww+u? ?u? =qˉ?qˉ

于是得证,qˉ?qˉ也可以同理得证,结果是与之相同的。

模的定义

四元数的模的定义。与矢量的模类似,都表示长度或者绝对值的意思。四元数的绝对值是四元数到原点的距离。

|qˉ|=x2+y2+z2+w2??????????????√=w2+u? ?u? ????????√=qˉ?qˉ????√=qˉqˉ????√

逆的定义

四元数的逆通过qˉ?1qˉ=1来定义。在此式左右两端同时乘以 qˉ?,可以得到:

qˉ?1qˉqˉ?=qˉ?

由于格拉丝曼积符合结合率,左式后两项先乘,得到:

qˉ?1(qˉqˉ?)=qˉ?

再由上述的推论,可以得出:

qˉ?1(qˉ?qˉ)=qˉ?

由于点积是个实数,可以使用除法,所以最后简化成:

qˉ?1=qˉ?qˉ?qˉ

单位四元数的定义

所谓的单位量,都是指,任意原始量乘以单位量之后保持原始量不变。如对于实数而言,任意原始实数乘以实数1等于原始实数,因此1被定义为单位。

对于四元数同样,任意原始四元数qˉ乘以某个四元数qˉi,原始的四元数qˉ保持不变,则称此四元数qˉi为单位四元数。注意这里的乘以是指格拉丝曼积。根据格拉丝曼积的定义:

qˉqˉi=((wui→+wiu? +u? ×ui→),(wwi?u? ?ui→))

若希望结果保持不变,也就是结果仍然为:

qˉqˉi=qˉ=(w,u? )

可以推测

wi=1,u? i=0

也就是,单位四元数为:

qˉi=(0? ,1)

四元数的归一化

归一化也叫单位化,与矢量的概念一样,就是为了把长度调整到单位长度。现在单位四元数已经得知,其单位长度显然是1。我们也知道了模的计算方法,因此把四元数的归一化方法如下:

qˉ,=qˉ|qˉ|=qˉw2+u? ?u? ????????√

前述的单位复数表示形式为:e = cos(φ)+ sin(φ)i,因为此时,复数的长度为cos(φ)2+sin(φ)2=1,我们使用类似形式表示归一化的四元数:

qˉ=(s? in(φ)n? ,cos(φ))

也就是实部现在使用cos(φ)表示,虚部使用sin(φ)n? 表示,其中n? 是归一化的三维矢量,由于a? ?b? =|a? ||b? |cos(θab),因此有n? ?n? =1。让我们尝试计算此时的模:

|qˉ|=w2+u? ?u? ????????√=cos(φ)2+sin(φ)2(n? ?n? )???????????????????√=1

四元数映射_实数

我们可以将实数映射到四元数空间,所谓的映射,也就是说1对1的找到相应的存在,并且在不同空间施加施加相对应运算的法则时获得相同的结果。

例如,对应实数w,尝试进行如下映射:

qˉ=(0? ,w)

那么是否能够遵守相应的运算法则?也就是说,如果实数wa与实数wb均被映射到了四元数空间,那么它们的加法和乘法的结果是否还能映射回去,与原始空间的运算获得相同的结果?加法显然可以,这里进行乘法计算测试:

qˉa=(0? ,wa)

qˉb=(0? ,wb)

qˉaqˉb=(0? ,wa)(0? ,wb)=wawb?0? ?0? +wa0? +wb0? +0? ×0?

=>

qˉaqˉb=wawb+0? =(0? ,wawb)

显然,依照映射原则,可以将(0? ,wawb)映射回去变成wawb,与实数空间的计算结果相同。因此,此映射法则是可行的:

qˉ=(0? ,w)

四元数映射_矢量直接映射

与实数映射类似,我们也可以尝试将三维矢量映射到四元数。

对于矢量v? 映射法则如下:

qˉ=(v? ,0)

同样,加法运算显然可以映射回去,我们来计算乘法运算。

qˉa=(v? a,0)

qˉb=(v? b,0)

qˉaqˉb=(v? a,0)(v? b,0)=00?va→?vb→+0vb→+0va→+va→×vb→

=>

qˉaqˉb=(va→×vb→,?v? a?v? b)

可以看到,此时出现了之前两个四元数中均不存在的实部。因此这个映射法则是错误的。(本身两个向量之间如果直接使用多对多的格拉斯曼积式乘法也是没有意义的,所以结果可想而知。那么,是不是就不能映射矢量了?不是,只是映射法则错误。回想之前在在复数中也曾使用过格拉斯曼积式的乘法
如果矢量用e=cos(φ)+sin(φ)i表示,那么此时的e表示了一个二维旋转,向量的旋转可以使用格拉斯曼积形式的复数乘法e1?e2表示,因此,映射法则应该与角度相关。)

四元数映射_矢量间接映射

同态

先考虑一个同态概念:假设M,M′是两个乘集,也就是说M和M′是两个各具有一个封闭的具有结合律的运算的代数系统。φ是M射到M′的映射,并且任意两个元的乘积的像是这两个元的像的乘积,即对于M中任意两个元a,b,满足

φ(a*b)=φ(a)*φ(b);

也就是说,当a→φ(a),b→φ(b)时,a*b→φ(a)*φ(b),

那么这映射φ就叫做M到M′上的同态。我前面所解释的可以互相映射大致意思也就是同态。

四元数中的同态函数

为什么我们研究同态?因为这里涉及到我们希望出现的向量的旋转,一个三维空间的向量被旋转之后,它应该还是一个三维矢量。这个旋转函数应该满足以下要求:

①、旋转完的矢量长度应该保持不变。也就是

|φ(A)|=|A|

②、假如对向量A和B都执行φ旋转,那么向量A、B的夹角θ应该与φ(A)、φ(B)的夹角应该相同。由矢量点积我们知道A?B=|A||B|cos(θ),在满足①的情况下,也就是模的乘积已经不变,那么如果点积结果相同,也就能保证角度不变,也就是:

φ(A)?φ(B)=A?B

③、假如对向量A和B都执行φ旋转,那么旋转完毕的φ(A)、φ(B)的相对方向关系与A、B应该保持一致,也就是所谓的手向性应该保持一致(比如原来A的正左侧恰好B,旋转完毕应该还是在正左侧,仅有夹角是不够的,正右侧也是90度关系)。假如满足下面的式子,则手向性不变

φ(A)×φ(B)=A×B

接着,我们详细了解间接映射的步骤。如下图所示:

我们通过间接映射将矢量V映射到四元数,先如直接映射那样,映射成一个纯虚四元数,即(V? ,0),再执行φ()变换。简写为

φ(V)

由于

AB=wawb?ua?ub+wa?ub+wb?ua+ua×ub

此时的A、B均是纯虚四元数,因此wa=wb=0,可以简化为

AB=?ua?ub+ua×ub

由于A、B均是纯虚四元数,因此A?B=ua?ub,再由于四元数叉积和矢量叉积等价,因此上式可以转化为

AB=?A?B+A×B

如果存在这样的同态函数函数:

φ(A)φ(B)=φ(AB)

也就能满足②、③的要求。

[此处还可以解释得更明确,不过太过复杂了]


旋转四元数

最后,给出旋转四元数,也就是一直在追求的φ()函数。

qˉ=(s? in(θ2)n? ,cos(θ2))

它的含义是围绕n? 轴旋转θ角度。
具体旋转的方法使用以下函数:

pˉ,=qˉpˉqˉ?

其中pˉ是被旋转四元数,pˉ,是结果四元数。这里的qˉ?写成qˉ?1也是可以的,因为根据前述对逆的计算,对于此时模为1的qˉ来说,它们结果相同的。
具体推究方法以及证明方法过于复杂,就不在这里给出了。

总结

四元数是高阶复数的数学,它用在游戏中的作用主要是计算三维矢量的旋转,它使用先将矢量映射到纯虚四元数,再应用形如pˉ,=qˉpˉqˉ?,qˉ=(s? in(θ2)n? ,cos(θ2))的旋转函数。最后可以达成旋转目的。

前面的四元数性质不甚了解也没有太大关系,大概了解到是如何映射的也就可以了。甚至在Unity编程过程中你对四元数的映射关系不了解都无所谓。后续文章中我再介绍具体的用法。

时间: 2024-10-19 11:20:58

【Unity编程】四元数(Quaternion)与欧拉角的相关文章

【Unity编程】Unity中关于四元数的API详解

Unity中关于四元数的API详解 Quaternion类 Quaternion(四元数)用于计算Unity旋转.它们计算紧凑高效,不受万向节锁的困扰,并且可以很方便快速地进行球面插值. Unity内部使用四元数来表示所有的旋转. Quaternion是基于复数,并不容易直观地理解. 不过你几乎不需要访问或修改单个四元数参数(x,y,z,w); 大多数情况下,你只需要获取和使用现有的旋转(例如来自"Transform"),或者用四元数来构造新的旋转(例如,在两次旋转之间平滑插入). 大

[Unity Quaternion]四元数Quaternion的计算方式

什么是Quaternion四元数 1843年,William Rowan Hamilton发明了四元数,但直到1985年才有一个叫Ken Shoemake的人将四元数引入计算机图形学处理领域.四元数在3D图形学中主要用于旋转,骨骼动画等. 简单地来说,四元数描述了一次旋转:绕任意一个轴(V)旋转一个弧度(θ). 那么四元数q就与(V,θ)两个参数有关. 具体公式: q = (sin(θ / 2) * V,cos(θ / 2) ) q = (sin(θ / 2) * x,sin(θ / 2) *

unity 使用四元数旋转向量

1.向量转换为四元数 Quaternion qua0=new Quaternion(); Debug.Log(qua0.eulerAngles);//输出:(0.0, 0.0, 0.0) Quaternion qua1=Quaternion.LookRotation(Vector3.forward); Debug.Log(qua1.eulerAngles);//输出:(0.0, 0.0, 0.0) Quaternion qua2=Quaternion.LookRotation(new Vecto

【Unity编程】欧拉角与万向节死锁(图文版)

万向节死锁(Gimbal Lock)问题 上文中曾经说过,欧拉旋转的顺规和轴向定义,自然造就了"万向节死锁"问题.本文主要来探索它自然形成的原因. 陀螺仪 首先,我们来了解Gimbal 究竟是个什么玩意儿.下面来自维基百科中关于Gimbal的一段引述: 平衡环架(英语:Gimbal)为一具有枢纽的装置,使得一物体能以单一轴旋转.由彼此垂直的枢纽轴所组成的一组三只平衡环架,则可使架在最内的环架的物体维持旋转轴不变,而应用在船上的陀螺仪.罗盘.饮料杯架等用途上,而不受船体因波浪上下震动.船

Unity编程标准导引-3.3 Transform

每个游戏对象(GameObject),其存在于游戏世界,都有一个位置.朝向.大小等基本定位信息:其存于Hierarchy面板,也存在与其他GameObject的相对关系,如父子关系.兄弟关系.Unity中使用Transform来描述和操作这些属性. Transform的字面理解就是"变换"的意思,所有的GameObject,当其被创建完成之后,均自动创建了这样一个变换组件,你不需要手动创建这个组件,而且无法删除此组件.接下来,我们将变换组件的主要属性和功能一一进行解释. 3.3.1.层

四元数quaternion

四元数的简单方法运用四元数在Unity3D中的作用就是拿来表示旋转. AngleAxis 创建一个旋转,绕着某个轴旋转,返回结果是一个四元数. 跟ToAngleAxis实现的是相反的功能. Angle 返回两个旋转值(四元数)之间的角度,返回值是float类型的角度值. (不知道这个值算出来后有什么用) Dot 点乘,我也不太理解其意义. 参见 eulerAngles 返回表示旋转的欧拉角度(Vector3 即3个值) (如果调用的是某个物体,则表示该物体当前位置是从原始位置怎么旋转过来的, 其

【Unity编程】Unity动画系统(一)

Unity动画系统 Unity动画系统,也称为"Mecanim",提供了以下功能: 简单的工作流程,设置动画的所有元素,包括对象,角色和属性. 支持导入外部创建的动画片段和使用内置动画编辑器制作的动画片段. 人型动画重新定位,动画角色的运动控制可以被所有的角色模型共享,即角色的外观(SkinedMesh)和运动(Animator)是分离的,它们互相组合之后形成最终的动画. 用于编辑动画状态的的简化工作流程,即动画控制器. 方便预览动画片段,以及片段之间的插值过渡. 这使得动画师可以独立

Unity——编程中常见问题(永不止续)

问题:`System.IO.File' does not contain a definition for `ReadAllBytes' 解决: 这是由于unity的build settings默认指定web player平台,而非standalone build. file->build settings->选中相应的平台->switch platform即可. 参考资料: http://answers.unity3d.com/questions/1019958/systemiofil

四元数 Quaternion

最近在重写自己游戏引擎的场景管理模块,重温了一下有关四元数的一些知识,在此做一下简单的笔记. 四元数可以用来准确地描述三维矢量的旋转,并且可以有效地表达多个旋转操作的叠加,因此在三维游戏引擎的场景管理模块中,四元数具有很重要的意义. 本文为大便一箩筐的原创内容,转载请注明出处,谢谢:http://www.cnblogs.com/dbylk/ 一.定义 形如A = ai + bj + ck + d的复数称为四元数,其中i.j.k为虚数(称为四元数的基元),a.b.c.d为实数. 二.常见性质 1.