Unity學習筆記10——旋轉(四元數和尤拉角)
在Unity中,所有物體即使是空物體,也至少繫結Transform這個元件,這個元件有三個屬性:position、rotation、scale,它們分別用於控制物體的平移、旋轉和縮放三種變化,而其中最為複雜的一種就是旋轉,它就對應於transform元件中的rotation屬性,這個屬性的型別其實就是四元數。
引言:
常用的控制旋轉的方法有:矩陣旋轉和尤拉旋轉,還有本篇要介紹的重點四元數,它也是實現旋轉的方式之一。下面簡單介紹一下前面的兩種實現方式:
1.矩陣旋轉:使用一個4*4的矩陣來表示繞任意軸旋轉時的變換矩陣,這個矩陣具有的特點:乘以一個向量的時候只改變向量的方向而不會改變向量的大小
優點:旋轉軸可以是任意向量;
缺點:進行旋轉其實我們只需要知道一個向量和一個角度,4個值的資訊,而旋轉變換矩陣使用了4*4=16個元素;
變換過程增加乘法運算量,耗時;
2.尤拉旋轉:在旋轉時,按照一定的順序(例如:x、y、z,Unity中的順序是z、x、y)每個軸旋轉一定的角度,來變換座標或者是向量,實際上尤拉旋轉也可以理解為:一系列座標旋轉的組合;
優點:只需使用3個值,即三個座標軸的旋轉角度;
缺點:必須嚴格按照順序進行旋轉(順序不同結果就不同);
容易造成“萬向節鎖”現象,造成這個現象的原因是因為尤拉旋轉是按順序先後旋轉座標軸的,並非同時旋轉,所以當旋轉中某些座標重合就會發生萬向節鎖,這時就會丟失一個方向上的選擇能力,除非打破原來的旋轉順序或者三個座標軸同時旋轉;
由於萬向節鎖的存在,尤拉旋轉無法實現球面平滑插值;
一、四元數:
根據前面所述,我們知道了四元數是用於表示旋轉的一種方式,而且transform中的rotation屬性的資料型別就是四元數,那麼四元數該如何表示呢?從本質上來講,四元數就是一個高階複數,也就是一個四維空間。
普通的低階複數形式一般是:x = a + bi,其中a、b為實數,而i則是虛數單位,而且存在i^2 = -1這樣的運算規律,用座標表示時其實就是由實軸和虛軸構成的二維空間。
說四元數是高階複數,是因為它一般表示為:x = a + bi + cj + dk,其中i、j、k都是虛數單位,所以也都滿足:i
關於四元數的優缺點:
優點:避免萬向節鎖現象;
可繞任意過原點的向量旋轉;
可提供球面平滑插值;
缺點:比尤拉旋轉複雜,多了一個維度;
不夠直觀;
二、四元數與尤拉角:
根據上述,我們可以這樣表示一個四元數:q = w + xi + yj + zk,為了與三維旋轉聯絡起來,可以簡化表示為:q = ((x,y,z),w) = (v,w),其中v是一個向量,而w是個實數。此外,向量可以看做實部為0的四元數,而實數亦可以看做虛部為0的四元數。
假設我們想讓點P繞單元向量u = (x,y,z)表示的旋轉軸轉θ角度,具體步驟:
1.將點P座標轉換到四元數空間:P = (P,0);
2.使用q=((x,y,z)sinθ2,cosθ2) 來執行這個旋轉;
3.旋轉後的結果p'的座標為:p′=qpq−1;
三、實際應用:
上述講解的是四元數的原理,但是在實際的使用中並沒有那麼複雜,我們只要呼叫Unity為我們提供的介面來修改旋轉角度即可,例如為物件直接設定一個旋轉值:
float speed = 100.0f;
float x;
float z;
void Update () {
if(Input.GetMouseButton(0)){//滑鼠按著左鍵移動
y = Input.GetAxis("Mouse X") * Time.deltaTime * speed;
x = Input.GetAxis("Mouse Y") * Time.deltaTime * speed;
}else{
x = y = 0 ;
}
//旋轉角度(增加)
transform.Rotate(new Vector3(x,y,0));
/**---------------其它旋轉方式----------------**/
//transform.Rotate(Vector3.up *Time.deltaTime * speed);//繞Y軸 旋轉
//用於平滑旋轉至自定義目標
pinghuaxuanzhuan();
}
//平滑旋轉至自定義角度
void OnGUI(){
if(GUI.Button(Rect(Screen.width - 110,10,100,50),"set Rotation")){
//自定義角度
targetRotation = Quaternion.Euler(45.0f,45.0f,45.0f);
// 直接設定旋轉角度
//transform.rotation = targetRotation;
// 平滑旋轉至目標角度
iszhuan = true;
}
}
bool iszhuan= false;
Quaternion targetRotation;
void pinghuaxuanzhuan(){
if(iszhuan){
transform.rotation = Quaternion.Slerp(transform.rotation, targetRotation, Time.deltaTime * 3);
}
}
就像上述的程式碼中,在實際應用中我們只需通過Quaternion.Euler和Quaternion.Slerp來完成Rolation的賦值操作。