Unity中的歐拉角和四元數
筆記主要參考以下的博文
??Unity中的歐拉旋轉 - Andrew的游戲世界 - CSDN博客
【Unity編程】Unity中關于四元數的API詳解 - Andrew的游戲世界 - CSDN博客
Unity中的歐拉角
順規
在經典力學里,時常用zxz順規來設定歐拉角;照著第二個轉動軸的軸名,簡稱為x順規。另外,還有別種歐拉角組。合法的歐拉角組中,唯一的限制是,任何兩個連續的旋轉,必須繞著不同的轉動軸旋轉。因此,一共有12種順規。
Unity的歐拉角定義
Unity文檔中Transform.eulerAngles的定義,本身是一個Vector3,就是一個三維矢量, 分別含有xyz三個參數。
xyz代表了三個角度,它們定義了一組有序的旋轉,即圍繞z軸旋轉z度,然后圍繞x軸旋轉x度,然后圍繞y軸旋轉y度。你應該只去讀取或者直接設置這些數值,不要增加它們,因為當角度超過360度將會失敗。應該使用Transform.Rotate去替代執行旋轉操作。
在上述文檔中定義了在Unity中歐拉旋轉的順規,即先圍繞z軸旋轉,后繞x軸,最后繞y軸。
Unity內部使用四元數去執行旋轉,不會存儲歐拉角的累計值,因此它說超過360度會失敗是可以理解的,它用四元數執行完運算之后,會更新最后對應的歐拉角數值,而這個結果歐拉角只是代表了等值的旋轉變化結果,卻無法代表中間過程,由于歐拉角旋轉Z軸361度與1度有一樣的結果,它便最后只存儲了1度,以便于我們觀察和使用。
歐拉旋轉的軸
Unity中的Transform文檔有有這樣一個函數
public void Rotate(Vector3 eulerAngles, Space relativeTo = Space.Self);
這個函數提供了一個可選的相對空間坐標系參數:
Space.Self 局部坐標系,意味著本次歐拉旋轉以物體當前的局部坐標朝向為基礎出發執行旋轉。
Space.World 世界坐標系,意味著本次歐拉旋轉以物體當前的世界坐標朝向為基礎出發執行旋轉。
最重要的倒不是它有可選的世界坐標系,一般而言,常用的旋轉都是相對當前局部坐標系執行的。并且在本次歐拉旋轉過程中,它的相對軸是始終不變的。
比如我們可以指定一組歐拉旋轉(90,60,30),通過前述的順規我們知道,先繞Z軸旋轉30度,再繞X軸旋轉90度,再繞Y軸旋轉60度,雖然有這樣的順序,但是Z旋轉后相對X軸、Y軸,都是執行本組歐拉旋轉前的那個軸向,它沒有發生變動,所以我稱它為“當前軸”。
而什么情況下是相對軸不變的呢?舉個例子,指定(90,60,30)這組歐拉旋轉,執行Transform.Rotate(new Vector3(90,60,30))? ? 是執行了一次歐拉旋轉,在這過程中它的相對軸保持不變,但如果是這樣的旋轉方式:
Transform.Rotate(new Vector3(0,0,30));
Transform.Rotate(newVector3(90,0,0));
Transform.Rotate(new Vector3(0,60,0));
這就是執行了三次歐拉旋轉,在執行后兩次時其相對軸都會發生變化,顯然結果不是我們需要的(90,60,30)的歐拉角旋轉。
總結
Unity中的歐拉旋轉是按照Z、X、Y順規執行的旋轉,一組歐拉旋轉過程中,相對的軸向不會發生變化。 Transform.Rotate(new Vector3(90,60,30)),它代表執行了一組歐拉旋轉,它相對的是旋轉前的局部坐標朝向。正是這種順規和軸向的定義,導致了萬向節死鎖的自然形成。
Unity中的四元數
四元數實際上和矩陣類似,不同的只是它的表示方式以及運算方式。
那么在Unity里如何利用四元數進行旋轉呢?
首先是創建四元數,Unity里提供了非常多的方式來創建一個四元數。例Quaternion.AngleAxis(float angle, Vector3 axis),它可以返回一個繞軸線axis旋轉angle角度的四元數變換。我們可以一個Vector3和它進行左乘,就將得到旋轉后的Vector3。在Unity里只需要用一個“ * ”操作符就可以進行四元數對向量的變換操作,相當于我們上述講到的p′=qpq?1操作。如果我們想要進行多個旋轉變換,只需要左乘其他四元數變換即可,例如
Vector3?newVector?=?Quaternion.AngleAxis(90,?Vector3.up)?*?Quaternion.LookRotation(someDirection)?*?someVector;
Unity中提供了關于四元數旋轉的API,即Quarternion類。Quaternion(四元數)用于計算Unity旋轉。它們計算緊湊高效,不受萬向節鎖的困擾,并且可以很方便快速地進行球面插值。 Unity內部使用四元數來表示所有的旋轉。四元數是基于復數的,因而理解起來難度很大,好在我們并不需要去給出參數的值,而是只需要獲取和使用現有的旋轉(例如來自“Transform”),或者用四元數來構造新的旋轉(例如,在兩次旋轉之間平滑插入)即可。
一些常用的函數:
Quaternion.LookRotation? ?前方、上方標注矢量法創建四元數
Quaternion.Angle? ??計算兩個四元數前方矢量之間的夾角度數
Quaternion.Euler??歐拉角表示創建四元數
Quaternion.Slerp??球面插值
Quaternion.FromToRotation??A向到B向相對旋轉表示法創建四元數
Quaternion.identity? 為單位四元數,即默認的無旋轉狀態
Quaternion 是一個結構體,本身成員變量相對簡單,可以作為函數參數高效傳遞。
Unity的方向
Unity中使用左手坐標系,假如把世界坐標系跟東南西北進行結合起來看,大致如下圖所示:
+x? ? ? ? ?東(右)????????????????????+y? ? ? ? ?上? ? ? ? ? ? ? ? +z? ? ? ? ? ? 前(北)
-x? ? ? ? ? 西(左)????????????????????-y? ? ? ? ? 下? ? ? ? ? ? ? ? -z? ? ? ? ? ? ?后(南)
以自己身體為例,你站立在地面上,面朝北方,此時就是默認方向,也就是Unity中的方向就是面向+Z軸方向,那么此時+X軸在東方,+Y軸對應正上方。此時對應的歐拉角是(0,0,0),此時對應的前方矢量是(0,0,1),上方矢量是(0,1,0)。這里的上下左右的概念與Vector3類、Transform類中的相應的方向函數也是相對應的。
確定方向的方法
1.歐拉角表示法
Unity中的歐拉旋轉方向為固定的zxy順規。
2.前方、上方標定矢量法
根據對局部坐標軸的描述, 構造對應的代表旋轉程度的四元數。
指的是從默認方向到LookRotation參數的變化量。例如? ?Quaternionq2 = Quaternion.LookRotation(Vector3.down ,Vector3.forward); 是指從Unity默認方向到這個的變化量,按照左手坐標系,繞x轉90,從Z+到Y-。
3.?繞軸旋轉界定法
使物體沿某一指定的軸向旋轉一定的角度。這個方法也可以確定一個相對旋轉,它以從默認方向(此時前方+Z,上方+Y)出發,沿著指定的軸向進行指定角度的旋轉。旋轉后的前方和上方是確定的,所以可以確定朝向。
4.A向到B向相對旋轉表示法
從A向到B向的相對旋轉,這種表示了一個旋轉的相對變化。比如A為(0,1,0),B為(0,0,1),也就是相對旋轉量代表原來的上方被旋轉到了前方,這樣的一個四元數也可以用歐拉角表示成(90,0,0),也就是沿著+X軸旋轉了90度。
利用幾種表示方法在Untiy中創建四元數
Quaternion q1=Quaternion.Euler(90, 0, 0);? ? ?//方法1—歐拉角表示
Quaternion q2 = Quaternion.LookRotation(Vector3.down ,Vector3.forward);? //方法2—前方上方標注矢量法
Quaternion q3 = Quaternion.AngleAxis(90,Vector3.right);? //方法3—繞軸旋轉界定法?
Quaternion q4 = Quaternion.FromToRotation(Vector3.up, Vector3.forward);? ?// 方法4—A向到B向相對旋轉表示法
成員變量
eulerAngles 歐拉角,返回當前四元數所對應的歐拉角
this[int] 可以使用類似數組和下標的形式從四元數中獲取四個四元數參數
四元數的具體參數x,y,z,w分量一般不手動設置,易出錯。
identity 單位四元數,也就是默認的無旋轉狀態,此時與世界坐標相同,前方指向+Z,上方指向+Y
成員函數
靜態函數
1.static float Angle(Quaternion a, Quaternion b)? ?計算兩個四元數前方矢量之間的夾角度數
2.static Quaternion AngleAxis(float angle, Vector3 axis)? ?構建一個四元數,它表示沿著一個軸旋轉固定角度,即上述表示法3
3.static float Dot(Quaternion a, Quaternion b)? ?計算兩個四元數之間的點積,返回一個標量,這個函數一般用不到
4.static Quaternion Euler(float x, float y, float z)? 構建一個四元數,用歐拉角度表示
5.static Quaternion FromToRotation(Vector3 fromDirection, Vector3 toDirection)??構建一個四元數,它表示從指向fromDirection方向到指向toDirection方向的相對旋轉量,見上述表示法4
6.static Quaternion Inverse(Quaternion rotation)??構建一個四元數,它是指定的四元數的逆,也就是逆向旋轉,比如原四元數表示相對+X軸旋轉了90度,那么此函數結果就是相對+X軸旋轉了-90度
7.static Quaternion Lerp(Quaternion a, Quaternion b, float t)? ? 構建一個四元數,表示從四元數a到b的球面插值,所謂的插值也就是中間旋轉量,從a作為起點,此時對應t為0,到b為終點,此時對應t為1。當t取0-1之間的小數時,就代表了中間的插值結果。這個方法與Slerp相同,計算速度快,但是精度低,如果相對旋轉變化量很小,則效果不理想
8.static Quaternion LerpUnclamped(Quaternion a, Quaternion b, float t)??與Lerp相同,區別是,Lerp的t值會被鉗制在[0,1]之間,而此方法則不會,t允許超出計算
9.static Quaternion LookRotation(Vector3 forward, Vector3 upwards = Vector3.up)??構建一個四元數,使用前方上方矢量確定朝向,也就是上述表示法2
10.static Quaternion RotateTowards(Quaternion from, Quaternion to, float maxDegreesDelta)??構建一個四元數,表示從一個四元數from(的前方)向著另外一個四元數(的前方)旋轉,但不能超出指定的角度,也就是如果兩個前方矢量夾角超過指定角度,則旋轉到達指定角度時就停止,若是夾角本身不足的話,則結果直接為目標四元數to,與上述表示法4的意思很接近
11.static Quaternion Slerp(Quaternion a, Quaternion b, float t)??球面插值,與Lerp功能相同,t值也被鉗制,計算精度高,但是速度相對較慢
12.static Quaternion SlerpUnclamped(Quaternion a, Quaternion b, float t)??與Slerp功能相同,只是t值不被鉗制,允許超出計算
13.static Quaternion operator * (Quaternion lhs, Quaternion rhs)??乘法運算符重載,當表示兩個連續的旋轉時,可以使用lhs * rhs的形式得出連續旋轉的結果,lhs為左值,rhs為右值。注意左值是先進行的旋轉,疊加右值旋轉。用法示例:lhs = lhs * rhs;
14.static Vector3 operator *(Quaternion rotation, Vector3 point)??乘法運算符重載,表示對一個矢量point施加旋轉rotation,得出旋轉后的結果矢量。用法示例:Vector3 result=rotation * point
對于一些方法的詳細描述可以參考
Unity中的四元數類詳解【詳解】 - qq_36684665的博客 - CSDN博客
Unity3D入門篇——Quaternion類 - u014086857的專欄 - CSDN博客