1. 程式人生 > >Unity學習筆記10——旋轉(四元數和尤拉角)

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

2=j2=k2=1。此外,這三個虛數單位還有以下特點:ij = k,jk = i,ki = j

關於四元數的優缺點:

優點:避免萬向節鎖現象;

           可繞任意過原點的向量旋轉;

           可提供球面平滑插值;

缺點:比尤拉旋轉複雜,多了一個維度;

           不夠直觀;

二、四元數與尤拉角:

        根據上述,我們可以這樣表示一個四元數: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=qpq1

三、實際應用:

        上述講解的是四元數的原理,但是在實際的使用中並沒有那麼複雜,我們只要呼叫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.EulerQuaternion.Slerp來完成Rolation的賦值操作。