【OpenGL】-005 四元数

【OpenGL】-005 Understanding Quaternions翻译

  本文是对《Understanding Quaternions》(https://www.3dgep.com/understanding-quaternions/)的翻译。

Understanding Quaternions

  本文中,将会使用便于理解的方式解释四元数的概念,四元数的表现形式,四元数支持的操作,对比矩阵、欧拉角、四元数的区别以及可以使用四元数替代矩阵或欧拉角的场景。
【OpenGL】-005 四元数

1、简介

  在计算机图形学中,使用变形矩阵表示空间中的位置或朝向。单独的一个变形矩阵可以用于表示一个物体的尺度变化或裁剪。可以认为变形矩阵式一种基础空间,当一个向量或点与变形矩阵相乘时,表示将该向量或点放入到该变形矩阵所描述的空间中。

  本文中,不会对变形矩阵进行详细讨论,可以参考上一篇文章《Matrices》(https://www.3dgep.com/3d-math-primer-for-game-programmers-matrices/)来获取关于矩阵的详细信息。

  四元数的概念是爱尔兰数学家Sir William Rowan Hamilton在1843年10月16日在都柏林提出的。Hamilton在与妻子一起去往Royal Irish Academy(爱尔兰皇家科学院)的路上,通过Royal Canal(皇家运河)上的Brougham Bridg(布鲁厄姆桥)的时候,突然有了一个戏剧性的发现,他立即将其刻在了桥上。

i2=j2=k2=ijk=1

【OpenGL】-005 四元数

2、复数

  在理解四元数之前,首先需要理解四元数的由来。四元数的概念是基于复数系统的。
   作为常见数字集合的补充,复数系统引入了一种新的数字集合称为虚数。虚数主要用于求解一些没有解的方程,例如:

x2+1=0

  为了求解这个表达式,x2=1必须成立,由于无论正数或负数的平方始终是正数,所以这个表达式被认为不可能成立。
  数学家们不能接受一个表达式没有解,所以虚数被发明出来用于解这类表达式。
  虚数具有如下的形式:
i2=1

  不要试图去真正的理解这个表达式,因为它是没有逻辑原因的。只需要接收i是一个平方之后等于1的东西就行。
  虚数的集合通常使用I表示。
  复数的集合(使用符号C表示)是实数和虚数的和,形式如下:
z=a+bia,bR,i2=1

  可以将所有实数理解为虚部b=0的复数,所有虚数理解为实部a=0的复数。

2.1 复数加减法

  复数可以通过分别对实部和虚部进行加减法来执行加减法。
加法

(a1+b1i)+(a2+b2i)=(a1+a2)+(b1+b2)i

减法
(a1+b1i)(a2+b2i)=(a1a2)+(b1b2)i

2.2 复数与标量乘法

  复数与标量的乘法是指用该标量分别乘以复数的实部和虚部:

λ(a+bi)=λa+λb

2.3 复数乘法

  复数的乘法遵循普通代数规则。

{z1 =(a1+b1i)z2 =(a2+b2i)z1z2 =(a1+b1i)(a2+b2i)=a1a2+a1b2i+b1a2i+b1b2i2=(a1a2b1b2)+(a1b2+a2b1)i

2.4 复数平方

  复数的平方表示复数乘以它自身。

{z=(a+bi)z2=(a+bi)(a+bi)=(a2b2)+2abi

2.5 共轭复数

  共轭复数是指具有相同的实部,虚部是原虚部的相反数的复数,用符号zz¯表示。

{z=(a+bi)z=(abi)

  共轭复数的乘法结果如下:
{z=(a+bi)z=(abi)zz=(a+bi)(abi)=a2abi+abi+b2=a2+b2

2.6 复数绝对值

  可以使用共轭复数来计算复数的绝对值(范数或模),复数的绝对值是该复数与其共轭复数的乘积的平方根,记为|z|:

{z=(a+bi)|z|=zz=(a+bi)(abi)=a2+b2

2.7 复数的商

  计算两个复数的商,可以将分子与分母分别乘以分母的共轭复数,得到计算结果。

{z1=(a1+b1i)z2=(a2+b2i)z1z2=a1+b1ia2+b2i=(a1+b1i)(a2b2i)(a2+b2i)(a2b2i)=a1a2a1b2i+b1a2ib1b2i2a22+b22=a1a2+b1b2a22+b22+b1a2a2b1a22+b22i

3、i的幂

  因为i2=1,所以i的其他次幂可以按如下计算:

{i0=1i1=ii2=1i3=ii2=ii4=i2i2=1i5=ii4=ii6=ii5=i2=1

  如果继续计算,结果符合以下规律:
(1,i,1,i,1,)

  i的负次幂也满足类似的规律:
{i0=1i1=ii2=1i3=ii4=1i5=ii6=1

  在二维笛卡尔坐标平面中,通过不断逆时针旋转90°,可以得到行如(x,y,x,y,x,)的序列。通过顺时针方向旋转90°,可以得到行如(x,y,x,y,x,)的序列。
【OpenGL】-005 四元数

4、复平面

  通过将实部映射到水平轴,虚部映射到垂直轴,可以将复数映射到二维平面中,称之为复平面。

【OpenGL】-005 四元数
  观察前面所讲到的序列,可以认为一个复数乘以i表示将该复数在复平面中逆时针旋转90°。
  验证如下。
  复平面中任意一点p,

p=2+i

  将p乘以i得到q
{p=2+iq=pi=(2+i)i=2i+i2=1+2i

  将q乘以i得到r
{q=1+2ir=qi=(1+2i)i=i+2i2=2i

  将r乘以i得到s
{r=2is=ri=(2i)i=2ii2=12i

  将s乘以i得到t
{s=12it=si=(12i)i=i2i2=2+i

  此时t精确的回到了起点p.将结果在复平面中画出来,可以得到如下的结果。
【OpenGL】-005 四元数
  通过将复数乘以i,可以得到顺时针旋转的序列。

4.1 旋转因子

  定义复平面中的旋转因子q

q=cosθ+isinθ

  任意的复数乘以旋转因子q
{p=a+biq=cosθ+isinθpq=(a+bi)(cosθ+isinθ)a+bi=acosθbsinθ+(asinθ+bcosθ)i

  也可以写成如下的形式:
[abba]=[cosθsinθsinθcosθ][abba]

  上式表示在复平面中绕原点逆时针旋转任意点。

5、四元数

  在以上关于复数和复平面的基础上,可以通过在复数的虚数中添加另外两个虚部,从而将点扩充值三维空间中。
  四元数的通用表达形式如下:

q=s+xi+yj+zk s,x,y,zR

  通过Hamilton著名的表达式:
i2=j2=k2=ijk=1

并且
ij=kjk=iki=jji=kkj=iik=j

  从上可以看出i,j,j之间的关系类似于笛卡尔坐标系下的单位向量的叉乘:
x×y=zy×z=xz×x=yy×x=zz×y=xx×z=y

  Hamiltion注意到i,j,k可以用来表示笛卡尔坐标系下的单位向量i,j,k,i2=j2=k2=1.
【OpenGL】-005 四元数
  上图表示了笛卡尔坐标系下的单位向量i,j,k的关系。

5.1 四元数的有序偶形式

  可以将四元数表示成有序偶形式:

q=[s,v] sR,vR3

  其中v也可以用它的独立分量表示:
q=[s,xi+yj+zk] s,x,y,zR

  用以上形式,可以更方便的对比四元数与复数之间的相似性。

5.2 四元数的加减法

  四元数的加减法类似于复数的加减法。

qa=[sa,a]qb=[sb,b]qa+qb=[sa+sb,a+b]qaqb=[sasb,ab]

5.3 四元数乘法

  同样可以计算四元数的乘法:

qa=[sa,a]qb=[sb,b]qaqb=[saa][sb,b]=(sa+xai+yaj+zak)(sb+xbi,ybj+zbk)=(sasbxaxbyaybzazb)+(saxb+sbxa+yazbybza)i+(sayb+sbya+zaxbzbxa)j+(sazb+sbza+xaybxbya)k

  计算结果是一个新的四元数。将虚部i,j,k用有序偶的形式表示:
i=[0,i] j=[0,j] k=[0,k]

  代入上式,同时认为[1,0]=1
[sa,a][sbb]=(sasbxaxbyaybzazb)[1,0]+(saxb+sbxa+yazbybza)[0,i]+(sayb+sbya+zaxbzbxa)[0,j]+(sazb+sbza+xaybxbya)[0,k]

  将表达式扩充成一组有序偶的和的形式:
[sa,a][sb,b]=[sasbxaxbyaybzazb,0]+[0,(saxb+sbxa+yazbybza)i]+[0,(sayb+sbya+zaxbzbxa)j]+[0,(sazb+sbza+xaybxbya)k]

  执行四元数单位向量乘法并提取同类型,可以将上式重写成如下形式:
[sa,a][sb,b]=[sasbxaxbyaybzazb,0]+[0,sa(xbi+ybj+zbk)+sb(xai+yaj+zak)+(yazbybza)i+(zaxbzbxa)j+(xaybxbya)k]

  上式给出了两个有序偶的和,其中第一个是一个实四元数,第二个是一个虚四元数。这两个有序偶可以组合成一个有序偶:
[sa,a][sb,b]=[sasbxaxbyaybzazb,sa(xbi+ybj+zbk)+sb(xai+yaj+zak)+(yazbybza)i+(zaxbzbxa)j+(xaybxbya)k]

  代入:
a=xai+yaj+zakb=xbi+ybj+zbkab=xaxb+yayb+zazba×b=(yazbybza)i+(zaxbzbxa)j+(xaybxbya)k

  可得:
[sa,a][sb,b]=[sasbab,sab+sba+a×b]

  上式是四元数乘法的通用表达式。

5.4 实四元数

  实四元数是带有0向量的四元数。

q=[s,0]

  两个实四元数的乘积是另一个实四元数。
qa=[sa,0]qb=[sb,0]qaqb=[sa,0][sb,0]=[sasb,0]

  这类似于两个虚部为0的复数的乘法:
z1=a1+0iz2=a2+0iz1z2=(a1+0i)(a+2+0i)=a1a2

5.5 四元数与标量乘法

  四元数与标量的乘法遵守如下规则:

q=[s,v]λq=λ[s,v]=[λs,λv]

  可以通过将标量视为一个实四元数的形式来验证:
q=[s,v]λ=[λ,0]λq=[λ,0][s,v]=[λs,λv]

5.6 纯四元数

  与实四元数类似,Hamiltion将纯四元数定义为标量部分为0的四元数。

q=[0,v]

  或写成分量形式:
q=xi+yj+zk

  可以执行两个纯四元数的乘法如下:
qa=[0,a]qb=[0,b]qaqb=[0,a][0,b]=[ab,a×b]

5.7 四元数的加法形式

  可以将任意四元数表示成一个实四元数与一个纯四元数的加法:

q=[s,v]=[s,0]+[0,v]

5.8 单位四元数

  对任意一个矢量v,可以将其表示成它的标量模和它的方向:

v=vv^ where v=|v| and |v^|=1

  将纯四元数的概念与上式相结合,可得:
q=[0,v]=[0,vv^]=v[0,v^]

  单位四元数是标量部分为0,且矢量部分是单位向量的四元数。
q^=[0,v^]

5.9 四元数的二元形式

  将单位四元数与四元数的加法形式相结合,可以得到一种类似于复数表现形式的表示四元数的方式:

q=[s,v]=[s,0]+[0,v]=[s,0]+v[0,v^]=s+vq^

  上式提供了一种类似于复数表示方式的方法来表示四元数:
z=a+biq=s+vq^

5.10 共轭四元数
  共轭四元数可以通过将矢量部分取反得到:

q=[s,v]q=[s,v]

  一对共轭四元数的乘积:
qq=[s,v][s,v]=[s2vv,sv+sv+v×v]=[s2+vv,0]=[s2+v2,0]

5.11 四元数的范数

  类似于复数的范数的计算方式:

|z|=a2+b2zz=|z|2

  四元数的范数或模的计算方法定义如下:
q=[s,v]|q|=s2+v2

  四元数的范数可表示成:
qq=|q|2

5.12 四元数归一化

  可以使用四元数的范数来对四元数进行归一化。四元数的归一化是指四元数除以它的模|q|

q=qs2+v2

  例子:归一化以下四元数:
q=[1,4i+4j4k]

  首先,计算该四元数的模:
|q|=12+42+42+(4)2=49=7

  然后,将四元数除以它的模,得到归一化的四元数:
q=q|q|=1+4i+4j4k7=17+47i+47j47k

5.13 四元数的逆

  四元数的逆记为q1。四元数的逆等于该四元数的共轭四元数除以模的平方:

q1=q|q|2

  证明过程如下:
qq1=[1,0]=1

  两边同时乘以共轭四元数:
qqq1=q

  代入后可得:
|q|2q1=q

q1=q|q|2

  对于单位模长的四元数其模长为1,那么:
q1=q

5.14 四元数的点乘

  类似于复数的点乘,四元数的点乘可以按如下规则计算:

q1=[s1,x1i+y1j+z1k]

q2=[s2,x2i+y2j+z2k]

q1q2=s1s2+x1x2+y1y2+z1z2

  可以使用四元数的点乘来求两个四元数之间的夹角:
cosθ=s1s2+x1x2+y1y2+z1z2|q1||q2|

  对于单位长度的四元数,
cosθ=s1s2+x1x2+y1y2+z1z2

6、旋转

  类似于二维复平面总的旋转因子,

q=cosθ+isinθ

对于三维空间中的四元数旋转形式如下:
q=[cosθ,sinθv]

  首先,扩展矢量p到纯四元数形式:
p=[0,p]

&emap; q是单位模长的四元数:
q=[s,λv^]

  然后,
p=qp=[s,λv^][0,p]=[λv^p,sp+λv^×p]

  结果是一个既有标量部分又有矢量部分的四元数。
  首先讨论特殊情况,p垂直与v^,此时二者的点乘结果为0,λv^p=0,于是结果变成了纯四元数形式:
p=[0,sp+λv^×p]

此时,使点pv^,只需将s=cosθλ=sinθ代入
p=[0,cosθp+sinθv^×p]

示例,将矢量p绕Z轴旋转45°,那么四元数q
q=[cosθ,sinθk]=[22,22k]

选择一个符合垂直于k这一特殊要求的矢量p:
p=[0,2i]

计算qp的乘积:
p=qp=[22,22k][0,2i]=[0,222i+222k×i]=[0,2i+2j]

这是一个表示绕k轴旋转45°的纯四元数。此时,结果的模如下:
|p|=22+22=2

与我们所期望的结果一致。
【OpenGL】-005 四元数
  然后在讨论与p不正交的四元数,以与p成45°角的四元数为例:
v^=22i+22kp=2iq=[cosθ,sinθv^]p=[0,p]

将矢量p乘以q:
p=qp=[cosθ,sinθv^][0,p]=[sinθv^p,cosθp+sinθv^×p]

v^,p,θ=45代入:
p=[22(22i+22k)(2i),222i+22(22i+22k)×2i]=[1,2i+j]

结果不再是一个纯四元数,并且模不再等于2。
【OpenGL】-005 四元数

但是,以上并没有错误。Hamilton发现如果在qp之后乘以q的逆,将得到一个纯四元数并且该四元数的模也将与被旋转向量相等。
首先,计算q1:

q=[cosθ,sinθ(22i+22k)]

q1=[cosθ,sinθ(22i+22k)]

θ=45时:
q1=[22,22(22i+22k)]=12[2,ik]

qpq1代入:
qp=[1,2i+j]qpq1=[1,2i+j]12[2,ik]=12[2(2i+j)(ik),i+k+2(2i+j)i+2j+k]=[0,i+2j+k]

结果是纯四元数,其模为
|p|=12+22+12=4=2

可见,其模长与p一致。
【OpenGL】-005 四元数
结果是一个纯四元数并且模长与初始的矢量一致,但该矢量被旋转了90而不是45°,是预期的角度的两倍。因此,为了正确的绕轴v^旋转矢量p角度θ,必须按照一半的角度构造旋转四元数:
q=[cos12θ,sin12θv^]

上式是旋转四元数的通用形式。

7、四元数插值

 &esmp;在计算机图形学中使用四元数的一个重要原因是四元数可以很好的表示空间中的旋转。
  四元数可以很好的克服在使用欧拉角等方式表示旋转三维空间中的点时遇到的类似万向节问题等不足。
  使用四元数,可以定义很多种表示三维空间中的旋转的方法。第一种叫做SLERP,可以很平滑的在两个四元数之间插入一个点。第二种方法是SLERP的扩展,叫做SQAD,用于在定义的路径上的一系列点中插值。

7.1 SLERP