1200字范文,内容丰富有趣,写作的好帮手!
1200字范文 > 零基础理解四元数 每个游戏入门开发必备知识

零基础理解四元数 每个游戏入门开发必备知识

时间:2020-09-13 22:45:55

相关推荐

零基础理解四元数 每个游戏入门开发必备知识

废话不多说,直接进入正题

数学好的大佬直接看原文,此文基本照搬原文,加入了一点注解,公式比较清晰!!!!!

原文地址

复数

在我们能够完全理解四元数之前,我们必须先知道四元数是怎么来的。四元数的根源其实是复数。

除了知名的数集(自然数、整数、实数、分数)之外,复数系统引入了一个新的数集——虚数。虚数的发明是为了解决一些特定的无解的方程,例如:

x2+1=0

要解决这个等式,必须让x2=−1,这当然是不行的,因为任意实数的平方都是非负数。

一般而言,数学家是不能忍受一个等式是无解的。于是,一个新的术语被发明了,它就是虚数,一个可以解决上面这个等式的数。

虚数有这样的形式:

i2=−1

不要为这个术语较真,因为逻辑上这个数是不存在的。只要知道i是一个平方等于-1的东西即可。

虚数的集合可以用 𝕀 来表示。复数的集合ℂ 是一个实数和一个虚数的和,形式如下:

z=a+bi a,b∈R, i2=−1

可以认为所有实数都是b=0的复数、所有虚数都是a=0的复数。

复数的加减

加法

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

减法

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

复数的系数缩放

λ(a1+b1i)=λa1+λb1i

复数的积

z1=(a1+b1i)

z2=(a2+b2i)

z1z2=(a1+b1i)(a2+b2i)=a1a2+a1b2i+b1a2i+b1b2i2

z1z2=(a1a2−b1b2)+(a1b2+b1a2)i

复数的平方

z=(a+bi)

z2=(a+bi)(a+bi)

z2=(a2−b2)+2abi

共轭复数

复数的共轭就是指把复数的虚数部分变成负的。共轭复数的符号是z¯ 或 z∗

z=(a+bi)

z∗=(a−bi)

复数和它的共轭复数的乘积是:

zz∗=(a+bi)(a−bi)=a2−abi+abi+b2=a2+b2

复数的绝对值

我们使用共轭复数来计算复数的绝对值:

z=(a+bi)

|z|=√(zz∗)=√((a+bi)(a−bi))=√(a2+b2)

两复数的商

z1=(a1+b1i)

z2=(a2+b2i)

z1z2=a1+b1ia2+b2i=(a1+b1i)(a2−b2i)(a2+b2i)(a2−b2i)

=a1a2−a1b2i+b1a2i−b1b2i2a22+b22

=a1a2+b1b2a22+b22+b1a2−a1b2a22+b22i

i的幂

如果i的平方等于-1,那么i的n次幂也应该存在:

i0=1

i1=i

i2=−1

i3=ii2=−i

i4=i2i2=1

i5=ii4=i

i6=ii5=i2=−1

如果按照这个顺序写下去,会出现这样一个模式:

(1,i,-1,-i,1,…)

一个类似的模式也出现在递增的负数幂:

i0=1

i−1=−i

i−2=−1

i−3=i

i−4=1

i−5=−i

i−6=−1

你可能已经在数学里头见过类似的模式,但是是以(x,y,-x,-y,x,…)的形式,这是在2D笛卡尔平面对一个点逆时针旋转90度时生成的;(x,-y,-x,y,x,…)则是在2D笛卡尔平面对一个点顺时针旋转90度时生成的。

复数平面

我们也能够把复数映射到一个2D网格平面——复数平面,只需要把实数映射到横轴、虚数映射到纵轴。

如前面的序列所示,我们可以认为,对一个复数乘以i,这个复数就在复数平面上旋转了90度。

让我们看看这是不是真的。我们随机地在复数平面上取一个点:

p=2+i

p乘以i后得到q:

q=pi=(2+i)i=2i+i2=−1+2i

q乘以i后得到r:

r=qi=(−1+2i)i=−i+2i2=−2−i

r乘以i后得到s:

s=ri=(−2−i)i=−2i−i2=1−2i

s乘以i后得到t:

t=si=(1−2i)i=i−2i2=2+i

t刚好是开始的p。如果我们把这些复数放到复数平面上,就得到下面的图:

我们也可以按顺时针方向旋转,只需要把上面的乘数i改成-i。

旋转数(Rotors)

我们也可以在复数平面上进行任意角度的旋转,只需要定义下面这个复数,改变θ角旋转任意度

特殊的复数 旋转数公式:q=cosθ+isinθ

任意的复数乘以q:

p=a+bi

q=cosθ+isinθ

pq=(a+bi)(cosθ+isinθ)

a′+b′i=acosθ−bsinθ+(asinθ+bcosθ)i

也可以写成矩阵的形式:

[a′b′−b′a′]=[cosθsinθ−sinθcosθ][ab−ba]

这也是一个在复数平面绕原点逆时针旋转任意点的方法。(译注:这句话应该是在说旋转矩阵)

四元数

了解了复数系统和复数平面后,我们可以额外增加2个虚数到我们的复数系统,从而把这些概念拓展到3维空间。

四元数的一般形式:

q=s+xi+yj+zk s,x,y,z∈ℝ

上面的公式是根据Hamilton的著名的表达式得到的:

i2=j2=k2=ijk=−1

以及:

ij=k jk=i ki=j

ji=−k kj=−i ik=−j

你可能已经注意到了,i、j、k之间的关系非常像笛卡尔坐标系下单位向量的叉积规则:

x×y=z y×z=x z×x=y

y×x=−z z×y=−x x×z=−y

Hamilton自己也发现i、j、k虚数可以被用来表达3个笛卡尔坐标系单位向量i、j、k,并且仍然保持有虚数的性质,也即i2=j2=k2=−1。

(i x j, j x k,k x i ,这几个性质的可视化)(x代表叉乘)

上图展示了如何用i、j、k作为笛卡尔坐标系的单位向量。

作为有序数的四元数

我们可以用有序对的形式,来表示四元数:

[s,v] s∈ℝ,v∈ℝ𝟛

其中的v,也可以用它各自独立的3个分量表示:

q=[s,xi+yj+zk] s,x,y,z∈ℝ

使用这种表示法,我们可以更容易地展示四元数和复数之间的相似性。

四元数的加减

和复数类似,四元数也可以被加减:

qa=[sa,a]

qb=[sb,b]

qa+qb=[sa+sb,a+b]

qa−qb=[sa−sb,a−b]

四元数的积

我们也可以表示四元数的乘积:

qaqb=[sa,a][sb,b]

=(sa+xai+yaj+zak)(sb+xbi+ybj+zbk)

=(sasb−xaxb−yayb−zazb)

+(saxb+sbxa+yazb−ybza)i

+(sayb+sbya+zaxb−zbxa)j

+(sazb+sbza+xayb−xbya)k

可以看到,四元数的乘积依然还是一个四元数。如果我们把虚数i、j、k

替换成有序对:

i=[0,i] j=[0,j] k=[0,k]

以及还有[1,0] = 1,将它们代入前面的表达式,就得到了:

qaqb=(sasb−xaxb−yayb−zazb)[1,0]

+(saxb+sbxa+yazb−ybza)[0,i]

+(sayb+sbya+zaxb−zbxa)[0,j]

+(sazb+sbza+xayb−xbya)[0,k]

再把这个表达式扩展成多个有序对的和:

qaqb=[(sasb−xaxb−yayb−zazb),0]

+[0,(saxb+sbxa+yazb−ybza)i]

+[0,(sayb+sbya+zaxb−zbxa)j]

+[0,(sazb+sbza+xayb−xbya)k]

如果把后3个四元数相加,并提取公共部分,就可以把等式改写成:

qaqb=[(sasb−xaxb−yayb−zazb),0]

+[0,sa(xbi+ybj+zbk)+sb(xai+yaj+zak)

+(yazb−ybza)i+(zaxb−zbxa)j+(xayb−xbya)k]

这个等式是2个有序对的和。第1个有序对是一个实四元数,第2个是一个纯四元数。这两个四元数也可以合并成一个:

qaqb=[(sasb−xaxb−yayb−zazb),

sa(xbi+ybj+zbk)+sb(xai+yaj+zak)

+(yazb−ybza)i+(zaxb−zbxa)j+(xayb−xbya)k]

如果把下面的表达式代入上面的等式:

a=xai+yaj+zak

b=xbi+ybj+zbk

a⋅b=xaxb+yayb+zazb

a×b=(yazb−ybza)i+(zaxb−zbxa)j+(xayb−xbya)k

(译注:注意,第三条和第四条并不是四元数的点积和叉积,而是向量的点积和叉积)

我们就得到了:

四元数的乘积一般式:qaqb=[sasb−a⋅b,sab+sba+a×b]

这就是四元数乘积的一般式。

实四元数

一个实四元数是一个虚部向量为零向量的四元数:

q=[s,0]

两个实四元数的乘积是另一个实四元数:

qa=[sa,0]

qb=[sb,0]

qaqb=[sa,0][sb,0]=[sasb,0]

这和2个虚部为0的复数的乘积几乎一样:

z1=a1+0i

z2=a2+0i

z1z2=(a1+0i)(a2+0i)=a1a2

四元数的系数缩放

我们也可以用一个系数(实数)去乘四元数:

q=[s,v]

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

我们可以用实四元数与普通四元数的乘积,来确认这个等式是否正确:

q=[s,v]

λ=[λ,0]

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

纯四元数

和实四元数相似,Hamilton也定义了纯四元数。纯四元数是s=0的四元数:

q=[0,v]

也可以写成下面的形式:

q=xi+yk+zk

然后是2个纯四元数的乘积:

qa=[0,a]

qb=[0,b]

qaqb=[0,a][0,b]=[−a⋅b,a×b]

四元数的加法形式

我们可以把四元数写成实四元数和纯四元数的和:

q=[s,v]

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

单位四元数

给定任意的向量v,我们可以把这个向量写成一个系数和一个单位方向向量的乘积:

v=vv̂ v=|v|,|v̂|=1

将这个定义和纯四元数的定义结合,就得到了:

q=[0,v]

=[0,vv̂]

=v[0,v̂]

然后,我们可以定义单位四元数了,它是一个s=0、v

为单位向量的四元数:

q̂=[0,v̂]

四元数的二元形式

我们现在可以把单位四元数的定义和四元数的加法形式结合到一起,就创造了一种新的四元数的表示法,这种表示法和复数的表示法形似:

q=[s,v]

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

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

=s+vq̂

这就给了我们一种和复数非常相似的四元数表示法:

z=a+bi

q=s+vq̂

共轭四元数

共轭四元数的计算,就是将四元数的虚向量取反:

q=[s,v]

q∗=[s,−v]

四元数和它的共轭四元数的乘积:

qq∗=[s,v][s,−v]

=[s2−v⋅(−v),−sv+sv+v×(−v)]

=[s2+v⋅v,0]

=[s2+v2,0]

四元数范数(类似向量模长,但是定义不一样)

回忆下复数范数的定义:

|z|=√(a2+b2)

zz∗=|z|2

类似的,四元数的范数可以这样定义:

q=[s,v]

|q|=√(s2+v2)

这也让我们可以这样表达四元数范数:

qq∗=|q|2

四元数规范化(类似向量归一化)

利用四元数范数的定义,就可以对四元数进行规范化。要让一个四元数规范化,只需要让这个四元数去除以它的范数:

q′=q / √(s * s+v * v)

举一个例子,让我们规范化下面这个四元数:

q=[1,4i+4j−4k]

第一步,先计算q的范数:

|q|=√(12+42+42+(−4)*(-4))

=√49=7

然后,q除以|q|:

q′=q / |q|

=(1+4i+4j−4k)7

=1/7+4/7i+4/7j−4/7k

四元数的逆

四元数的逆用q−1,因为电脑符号不方便,请仔细区分

表示。要计算四元数的逆,需要用四元数的共轭四元数去除以四元数的范数的平方:

q−1=q / (|q||q|)

为了证明这个式子,我们先根据逆的定义,有:

qq−1=[1,0]=1

两边都左乘共轭四元数q∗

:

q∗qq−1=q∗

将上文中的qq∗=|q||q|

代入这个式子,得到:

|q||q|q−1=q∗

q−1=q∗|q||q|

对于单位四元数,它的范数是1,所以可以写成:

q−1=q∗

四元数的点积

和向量的点积相似,我们也可以计算2个四元数的点积,只需要将各个对应的系数相乘,然后相加:

q1=[s1,x1i+y1j+z1k]

q2=[s2,x2i+y2j+z2k]

四元数的点积公式:q1⋅q2=s1s2+x1x2+y1y2+z1z2

我们也可以利用四元数点积,来计算四元数之间的角度差

cosθ=(s1s2+x1x2+y1y2+z1z2) / (|q1||q2|)

对于单位四元数,我们可以简化上面的等式:

cosθ=s1s2+x1x2+y1y2+z1z2

四元数旋转

前面我们定义了一个特殊的复数:旋转数。它是用来旋转2D复数平面的点的:

q=cosθ+isinθ

根据四元数和复数的相似性,应该有可能设计一个可以旋转3D空间的点的四元数:

q=[cosθ,sinθv]

让我们测试一下这个理论是否可靠,方法就是计算四元数q和向量p的积。第一步,我们把p写成纯四元数的形式:

p=[0,p]

以及单位四元数q:

q=[s,λv̂]

从而:

p′ = qp = [s,λv̂][0,p]

代入四元数的乘积一般式:qaqb=[sasb−a⋅b,sab+sba+a×b]

得出:p′ = [−λv̂⋅p,sp+λv̂×p]

我们可以看到结果是一个同时有系数、有虚向量的四元数。

让我们先考虑特殊的情形:p

与v̂正交。这种情况下,点乘部分等于0:−λv̂⋅p=0

。所以上面的四元数就变成了纯四元数:

p′=[0,sp+λv̂×p]

这时候,要使p

绕v̂旋转,我们只需要代入s=cosθ和λ=sinθ:

p′=[0,cosθp+sinθv̂×p]

现在,让我们找一个例子来测试上面的公式。譬如绕z轴(就是k轴)旋转45度,那么我们的四元数q就变成:

q=[cosθ,sinθk]

=[√2 / 2,(√2)k / 2 ]

然后,选一个特殊的p,并且p要和k轴正交,譬如把p放到i轴上,也就是:

p=[0,2i]

好了,现在计算下qp:

p′=qp

=[√2 / 2,(√2)k / 2 ] [0,2i] (此处代入四元数的乘积一般式)

=[0,(2 * (√2) / 2) * i+( 2 * (√2) /2 ) k×i] (此处*代表乘的意思,× 符号是叉乘,代入上面叉乘公式:a×b=(yazb−ybza)i+(zaxb−zbxa)j+(xayb−xbya)k)

=[0,(√2)i+(√2)j]

结果是一个绕了k轴转了45度的纯四元数。

我们可以确认这个四元数的向量部分的长度是:

i轴: (√2) ,j轴: (√2) , k轴: 0

p′=√ ( (√2)(√2)+(√2)(√2) )=2

这正是我们所期望的!

我们可以用图像展示旋转过程:

现在,让我们考虑更一般化的四元数,即和p不正交的四元数。现在让我们把p的向量部分偏移45度:

v̂=(√2) / 2 * i+(√2) / 2 * k (此处*代表乘的意思)

p=2i

q=[cosθ,sinθv̂]

p=[0,p]

然后算qp:

p′=qp

=[cosθ,sinθv̂][0,p]

[−sinθv̂⋅p,cosθp+sinθv̂×p]

代入我们设定的v̂,p

,以及θ=45∘

,得到:

p′=[−(√2)/2 * ((√2)/2 * i+(√2)/2 * k)⋅(2i),(√2)/2 * 2 * i+ ((√2)/2(2 * √2 * i+(√2)/2 * k)) ×(2 * i)] (此处*代表乘,⋅代表点乘,×代表叉乘)

=[−1,√2 * i+j] (此处*代表乘)

注意,算出来的结果已经不是纯四元数了,并且,它并没有旋转45度、范数也不再是2(反而变小了,变成(√3)

)

我们可以用图像展示旋转过程:

严格来说,这样子在3维空间中表示p′是不正确的。因为它其实是一个4维的向量!个人认为这样标注类似于我们把三维的向量投影到二维平面上,这里是四维投影到三维上。为了简单起见,我只将这个四元数的向量部分显示出来。

然而,还有一线生机。Hamilton发现(但没有正式宣布),如果对qp右乘q的逆,出来的结果是一个纯四元数,并且四元数向量部分的范数可以保持不变。让我们试试应用在我们的例子里。

首先计算:

q=[cosθ,sinθ((√2)/2 * i+(√2)/2 * k)] (此处*代表乘)

q−1=[cosθ,−sinθ((√2)/2 * i+(√2)/2 * k)] (此处*代表乘,-1代表逆)

(译注:这里q−1=q∗ 是因为q是单位四元数,q∗为共轭四元数)

再代入θ=45∘,得到:

q−1=[(√2)/2 ,−(√2)/2*((√2)/2 * i+ (√2)/2 * k)]

1/2 * [√2,−i−k]

现在,把前面算出来的qp再次拿出来:

qp=[−1,√2 * i+j]

qpq−1=[−1,√2 * i+j] * ((1/2) * [√ 2,−i−k]) (此处代入四元数乘积一般式。点乘,叉乘公式)

=1/2 * [−√2+ √ 2, i+k+2 * i+√2 * j−i+√2 * j+k]

=[0,i+√2 * j+k]

这下是纯四元数了,并且它的范数是:

i轴: 1,j轴: (√2) , k轴: 1

|qpq−1|=√(1+√2 * √2 + 1)=√4=2

这和原始的p的范数一致。

下面的图像展示了旋转结果:

所以我们可以看到,这个结果是一个纯四元数,并且原四元数的向量的范数也保持住了。但是还有一个问题:向量被旋转了90度而不是45度。这刚好是我们需要的度数的两倍!为了正确地让一个向量绕某个轴向量旋转某个角度,我们必须以目标角度的一半来计算。因此,我们构造了下面的四元数:

q=[cos((1/2) * θ),sin((1/2) * θ v̂)]

这就是旋转四元数的一般形式!

欧拉角转四元数:

设欧拉角 euler = (pitch,roll,yaw);

欧拉旋转顺序为 Z - X - Y

则每个轴转成四元数为:

qx = [cos(pitch/2),sin(pitch/2)i]

qy = [cos(roll /2),sin(roll/2)j]

qz = [cos(yaw /2),sin(yaw/2)k]

最后相乘得出结果

q = [cos(yaw /2),sin(yaw/2)k] [cos(pitch/2),sin(pitch/2)i] [cos(roll /2),sin(roll/2)j]

轴角转换四元数:

四元数还可以直观用轴角表示[x,y,z,theta]

xyz代表旋转轴v,theta代表旋转角

v = (x,y,z)

q = cos(theta/2) + sin(theta/2)v

总结

四元数相比欧拉角,可以避免万向节死锁问题,其次在游戏和很多硬件等营养方面有很广泛的使用,这篇文章是我从网上看到的,本来是修复网上盲目搬运文章公式的很多错误,但是后来找到了原作者并征得同意,原作者的文章没有错误,并且有四元数线性插值部分内容,此文加上部分个人当初思考中遇到的问题和难点,原文公式比较好理解。

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。