對遊戲開發中的四元數的一些理解
一、前言
遊戲開發裡四元數是用於旋轉計算,由於四元數比較難以理解,因而知乎上有“如何形象理解四元數”之類的問題,會有四維空間的介紹,甚至一些圖形化的表達方式,而四元數本身是一個抽象後的數學概念,個人覺得直接從四元數的代數性質出發,是理解四元數旋轉性質的更簡單、快速的方法。
本文對四元數一些性質的推導過程都是很基礎的代數運算,其中一些思路來自《大學物理》第23卷第4期的《三維轉動的四元數表述》一文。本文的大部分內容是從一些問題的理解出發,下面先列一下目錄:
1。四元數是超複數、四維向量、軸角?
2。四元數為什麼能表示旋轉?
3。為什麼四元數旋轉向量的公式是:
?
4。為什麼旋轉向量使用單位四元數?
5。四元數的逆或共軛表示什麼旋轉?
6。四元數取反表示什麼旋轉?
7。有沒有0四元數(0旋轉)?
8。程式碼裡如何儲存四元數?
9。程式碼裡如何計算向量旋轉?
10。四元數和標量、向量相乘有沒有意義?
11。四元數和四元數相乘有沒有意義?
12。四元數可以表示對一個向量的旋轉資訊,那麼如何表示物體的朝向(orientation)?
13。四元數如何進行父子空間朝向資訊的轉換?
14。四元數加法有沒有意義(四元數與角速度)?
15。兩個四元數如何進行插值?
二、四元數的一些性質
1.四元數定義
關於四元數的來源,可以看簡單下百度百科上關於
向量
的發展歷史
[1]
介紹,大致是:複數可以表達二維向量的運算,四元數則是將複數拓展到三維空間,四元數分為標量部分和向量部分,後來的三維向量的分析運算就脫胎於四元數的向量部分。
四元數的定義是:
,
四元數標量部分是
,向量部分是
,
是單位向量,因此四元數也可以寫成:
因此四元數是
標量和向量的組合
。
2.四元數與向量
對於2個四元數
,
,其乘積為:
把四元數寫出標量加向量的形式:
,
, 其中
,
,則有:
(叉乘的性質)
那麼上面四元數的乘積就變成:
而本身乘積也可以表示為:
所以可以得出:
同理可得:
(叉乘是反交換律)
聯合上面2式可得:
也就是說向量的運算可以用四元數運算獲得,如果把
當成標量為0的四元數,這種四元數其實也叫做
純四元數
。
而事實上標量和向量都是四元數的一個特例,所以
四元數的性質及運算規則也適用於向量
,四元數本身也有點乘和叉乘
[2]
:
此時再看四元數定義裡的
由於
是單位向量,那麼就可以得出:
如果
互相垂直,則:
剛好反向證明了
的定義。
3.四元數的模、共軛、逆
四元數的模
:
複數的共軛是實部相等,虛部相反,因此可以定義
四元數的共軛
:
令四元數
的逆為
,根據:
可得
四元數的逆
:
那麼可知單位四元數:
相應的,也可以定義
向量的共軛、逆
,令向量
,也就是個純四元數,則:
4.四元數的三角形式
對與四元數
,令:
,
,可得:
,因此對於
和
的關係可以給出極座標下的形式,即:
,
,在令向量
為向量
的單位向量,即:
,那麼就可以對四元數的表示式進行變形:
這就是
四元數的三角形式
,這與複數的三角形式:
是統一的,區別是一個是單位向量、一個是虛數單位,不過在代數運算上是一樣的:
。另外複數的三角形式也可以寫成指數形式:
,對應四元數也有一樣的
指數形式
:
5.四元數的一些運算規則
利用四元數運算的多項式展開方法,可以證明:
也就是
四元數滿足乘法的分配律、結合律,不滿足交換律
。
也就是
四元數乘積的共軛和逆是各個四元數共軛和逆的倒序的乘積
。
三、關於四元數的一些問題的理解
1.四元數是超複數、四維向量、軸角?
類比複數定義
,
,四元數可以叫做超複數。
由於四元數有4個維度的資料,所以本質上說是四維向量也沒有問題,而且確實能從四維空間進行一定的解釋,不過四元數設計之初應該不是直接從4維空間去考慮的,另外4維空間難以理解,因此遊戲開發中不必糾結與理解四元數的4維空間性質。
軸角是一個有明確幾何意思的概念,而四元數的機率更抽象一點,雖然四元數的三角形式代表的幾何意義其實是與軸角一樣的,但是軸角很難應用與實際運算中,相反四元數非常合適。
2.四元數為什麼能表示旋轉?
定義向量
和
,其夾角為
,
,
為垂直於
、
所在的平面單位向量,則有:
所以有:
也就是
,其中
就是一個三角形式的單位四元數,因此
存在一個單位四元數可以表示一個向量的旋轉操作
。
3.為什麼是四元數旋轉向量的公式是:
?
上一個問題的前提是:“
為垂直於
、
所在的平面單位向量”,那麼對於任意向量和四元數,有沒有表示旋轉的形式?
定義向量
和
,
,令
繞單位向量軸
旋轉
角度得到
,將
分解為平行於
的
和垂直與
的
,實際上只要旋轉
就行了,並且
滿足問題2的推導,如下圖:
則有:
令:
,才能得到上面的表示式,不過這裡用四元數的三角式:
在將原式透過三角函式二倍角公式公式變形:
可得等式:
消元得:
可取:
,
,那麼可得:
(如果取
則
和
只是取反,不影響最終結果),帶入上面假設的三角式就得到:
令四元數
,則
,因此可得結論:
也就是向量
繞單位向量軸
旋轉
角度得到向量
可由四元數運算獲得,這裡四元數是單位四元數,所以
任意單位四元數可將任意向量旋轉一定角度,旋轉軸是四元數向量部分,旋轉角度是四元數三角式裡角度的2倍
。
4.為什麼是旋轉向量使用單位四元數?
如果不是單位四元數,令
,則:
那麼新向量的模長會增大四元數模平方的倍數,不過這裡也可以發現,只要最終得到的向量在更改模大小就可以了,運算過程中四元數不是單位四元數也沒關係。
5.四元數的逆或共軛表示什麼旋轉?
令單位四元數
,則其逆或共軛為 :
也就是
繞相同的軸反向旋轉相同的角度
。
6.四元數取反表示什麼旋轉?
令單位四元數
,則取反為 :
是
繞相反的軸旋轉 #FormatImgID_133# 的角度
,從對向量的旋轉結果可以看出其旋轉效果是一樣的:
7.有沒有0四元數(0旋轉)?
如果四元數不產生旋轉效果,也就是
,則
,這也是引擎裡四元數“Identity”變數的定義,類似於向量的“Zero”變數。
8.程式碼裡如何儲存四元數?
因為遊戲使用四元數就是表示旋轉資訊,所以儲存內容的就是:
,也就是繞軸
旋轉
角度,其中有4個維度的資料,因此程式碼裡的資料結構是:
,其中:
。
同樣可由
得知四元數的旋轉角是:
,旋轉軸是:
。
9.程式碼裡如何計算向量旋轉?
只是向量旋轉的公式,真正程式碼計算還是得用到向量的點乘和叉乘,所以對公式進行展開,令單位四元數
:
因為
,也就是
,對上式在進行變形:
這就是
引擎裡最終使用的向量旋轉計算公式
,很簡潔,推導過程用到了雙重叉乘公式。
10.四元數和標量、向量相乘有沒有意義?
四元數和標量相乘就是各個分量和標量相乘,結果會改變四元數的模長,但並不改變四元數旋轉數值,四元數和向量相乘的結果是一個新的四元數,標量和向量部分都改變了。2者單獨使用對四元數代表的旋轉資訊的改變都沒有什麼明確意義,實際使用是存在與四元數的一些運算中,例如和標量相乘是在四元數的Normalize中,和向量相乘是在四元數旋轉向量的計算中。
11.四元數和四元數相乘有沒有意義?
文中開頭分析過四元數相乘的結果是:
,結果還是一個四元數,由於四元數的主要用途表示旋轉資訊,因此考慮對向量進行多次旋轉的情況,例如對向量
先後進行
3次旋轉,則旋轉結果應為:
令四元數
,
,也就是
四元數乘積對向量的旋轉相當於各個四元數依次按倒敘對向量進行旋轉,所以四元數和四元數相乘代表了旋轉的複合
。
12.四元數可以表示對一個向量的旋轉資訊,那麼如何表示物體的朝向(orientation)?
表達物體朝向最直觀的方法就是確定物體本地座標系的3個軸,以左手係為例就是:(Forward、Right、Up)3個軸(Unity裡是zxy軸,UE裡是xyz軸,所以這裡用這個寫法代替),可以從世界座標系的(Forward、Right、Up)旋轉一定角度獲得,旋轉的角度其實就是
尤拉角
,一般也叫做:翻滾角、俯仰角、偏航角(Roll、Pitch、Yaw):
當然尤拉角有很多形式,用不同順序的軸旋轉的結果是不一樣的,這裡用的(Roll、Pitch、Yaw)旋轉順序,旋轉軸就是世界座標軸,有意思的是按照相反的順序也能獲得完全一樣的旋轉結果,只不過旋轉軸是本地座標軸(尤拉角一般指按本地座標軸進行旋轉,其Yaw、Pitch、Roll的旋轉形式是與世界座標軸的Roll、Pitch、Yaw旋轉形式結果一致的):
所以用四元數表示朝向就是3次座標軸向量旋轉的複合,旋轉軸(Forward、Right、Up)用(x、y、z)代替,旋轉角(Roll、Pitch、Yaw)用(
、
、
)代替,則轉換公式為:
其中(x、y、z)滿足
,可求得
,
,
,
,帶入展開式即可。這裡的結果同維基百科上
[3]
是一致的,不過跟虛幻引擎裡的並不一致,原因是UE裡的尤拉角Roll、Pitch是順時針旋轉,而Yaw是逆時針旋轉的,這塊具體設計原因不太清楚。
UnrealEngine_Euler_To_Quaternion
因此
四元數透過複合運算最終可以表示物體的朝向資訊
,由於四元數的旋轉資訊就是軸和角,所以對任意2個朝向,可以直接找到一個軸並使一方繞軸旋轉一定角度獲得另一方,也就是說
2個朝向可以透過一次旋轉進行轉換
,這也是軸角旋轉的原理(尤拉旋轉定律)。
13.四元數如何進行父子空間朝向資訊的轉換?
四元數可以表示物體的朝向,其也代表了從世界空間座標系到物體本身座標系的旋轉資訊,因此在遊戲裡父子空間下物體朝向資訊的轉換就是四元數操作的複合。
對於2個物體的朝向
和
,物體1的朝向是直接從世界座標系旋轉來的,先考慮物體2的朝向是在物體1的本地座標系上旋轉而來的,也就是說物體1的座標系是物體2的“世界座標系”,想象一下先把A拉回0朝向,則物體2的朝向就是世界朝向,也就是應用了一個世界空間的
,然後在讓A應用本身的
,則此時物體2就回到了原來的朝向,因為物體1的旋轉會帶動物體2旋轉,因此物體2在世界空間的旋轉是:
,也就是:
世界旋轉=父世界旋轉*本地旋轉
在考慮物體2的朝向也是從世界座標系旋轉來的,則物體2相對與物體1的本地朝向的旋轉可以從上面的式子進行推導:
,也就是:
本地旋轉=父世界逆旋轉*世界旋轉
這裡應該在回顧一下問題11,會發現同樣是四元數連乘,但是意義卻是不一樣的,看過UE四元數程式碼應該注意到這個註釋:
也就是公式:
前者假定
是本地空間的,對應的是旋轉從本地轉世界,後者則假定
是世界空間的,對應的是旋轉的複合,當然最終都是獲得世界空間的新旋轉(這裡的世界空間也可以理解為父所在空間),使用的時候需要注意下這裡的區別。
14.四元數加法有沒有意義(四元數與角速度)?
四元數的加法就是各個分量相加,得到一個新的四元數,標量和向量部分都改變了,所以加法本身並不能代表什麼實際意義。
四元數加法一個實際應用的地方是在對四元數微分方程的求解上,考慮向量可以代表物體的位置,位置向量對時間求導就是速度,用於描述物體的線性運動,那麼四元數可以代表物體的朝向,則四元數的對時間求導應該也能描述物體的旋轉運動,由於遊戲裡一般使用顯示尤拉法求解運動微分方程,所以四元數旋轉運動方程形式應該是:
因此
四元數的加法可以體現在四元數的旋轉運動方程的求解中
,其中
(
是世界空間下的),關於這個公式的推導有很多方式,這裡介紹一個利用四元數本身的代數運算規則來推導的方法
[4]
:
角速度和線速度的關係為:
,而線速度也能由位置向量
對時間求導獲得:
,因此:
其中位置向量
可以定義一個四元數運算獲得:
,表示從物體0朝向下經由四元數
旋轉而來,所以
也代表了物體目前的朝向,因此可得:
其中
是常量,而
可由
求得:
帶入原式可得:
另
可得:
,也就是說形如
的四元數肯定是一個純四元數,也就是個向量,由向量的乘法運算可知:
由於
與
是垂直的,因此只有一個解就是:
,也就得到最終結果:
15.兩個四元數如何進行插值?
對兩個四元數進行插值有幾個思路,第一個是針對四元數的代數表示式進行直接插值,也就是針對標量部分和向量部分或者4個分量進行插值,第二個是將四元數理解成旋轉資訊,也就是旋轉軸加旋轉角,則2個四元數插值可以分別對旋轉軸和旋轉角進行插值,第三個是將四元數看成一個朝向,而2個朝向可以透過一個四元數的旋轉進行轉換,那麼就可以找到一個轉換四元數,然後對這個轉換四元數的旋轉角進行插值。
第一個思路,令四元數
和
,插值係數
,則插值後的四元數為:
第二個思路,令四元數
和
,插值係數
,則插值後的四元數為:
第三個思路,對於四元數
和
,插值係數 為
,轉換四元數為:
,則插值後的四元數為:
如果這個思路寫成四元數的指數形式的話就是:
這其實就是
四元數的Slerp插值公式
,對上面的推導公式進行進一步化簡可得:
如果令
,則最終的形式就是:
這就是實際計算時用到的四元數的Slerp公式,之所以叫Slerp是因為公式跟向量的Slerp是一樣的,而且角
正好也是四元數的點積,這也應徵了四元數的四維向量的性質。
然後看一下3個方案的效果,從左到右按照上面的思路順序來,最後一個是對尤拉角的線性插值效果:
第一個方案其實就是四元數的Nlerp(Lerp之後在Normalize一下),效果上跟第三個Slerp還有點區別的,Slerp因為是對旋轉角進行插值的,所以插值變化時角速度時不變的,看起來更平穩,Nlerp則是中間快2頭慢,具體可以參考向量的Lerp和Slerp的分析
[5]
,方案2的旋轉效果看起來是大概是一個先慢後快的過程,不過計算量比Slerp大一點。另外第二張圖可以明顯看見Slerp旋轉的路徑更簡單(可以看到XY軸旋轉角度小於180度),這是因為引擎在實際處理Slerp的時候會判斷插值的旋轉角,如果比較大那麼可以對轉換四元數進行取反,這樣旋轉的角度就會變小,旋轉路徑更短,這也是Slerp的一個優勢。
參考
^
向量
https://baike。baidu。com/item/%E5%90%91%E9%87%8F/1396519?fr=aladdin
^
四元數
https://baike。baidu。com/item/%E5%9B%9B%E5%85%83%E6%95%B0/5795379?fr=aladdin
^
Conversion between quaternions and Euler angles
https://en。wikipedia。org/wiki/Conversion_between_quaternions_and_Euler_angles
^
How to Integrate Quaternions
https://www。ashwinnarayan。com/post/how-to-integrate-quaternions/
^
四元數與三維旋轉
https://krasjet。github。io/quaternion/quaternion。pdf