天天看點

【Unity程式設計】Unity中的歐拉旋轉歐拉角的定義

歐拉角的定義

在寫這篇部落格之前,我搜尋了網上很多關于歐拉角的定義,發現大部分引用自維基百科的定義,我這裡也引述一下:

維基百科定義

萊昂哈德·歐拉用歐拉角來描述剛體在三維歐幾裡得空間的取向。對于任何參考系,一個剛體的取向,是依照順序,從這參考系,做三個歐拉角的旋轉而設定的。是以,剛體的取向可以用三個基本旋轉矩陣來決定。換句話說,任何關于剛體旋轉的旋轉矩陣是由三個基本旋轉矩陣複合而成的。

對于在三維空間裡的一個參考系,任何坐标系的取向,都可以用三個歐拉角來表現。參考系又稱為實驗室參考系,是靜止不動的。而坐标系則固定于剛體,随着剛體的旋轉而旋轉。參閱下圖。設定xyz-軸為參考系的參考軸。三個歐拉角: (αβγ),藍色的軸是xyz-軸,紅色的軸是XYZ-坐标軸。稱xy-平面與XY-平面的相交為交點線(綠色),用英文字母(N)代表。

zxz順規的歐拉角可以靜态地這樣定義:

  • α是x-軸與交點線的夾角,
  • β是z-軸與Z-軸的夾角,
  • γ是交點線與X-軸的夾角。
    【Unity程式設計】Unity中的歐拉旋轉歐拉角的定義

很可惜地,對于夾角的順序和标記,夾角的兩個軸的指定,并沒有任何正常。科學家對此從未達成共識。每當用到歐拉角時,我們必須明确的表示出夾角的順序,指定其參考軸。

實際上,有許多方法可以設定兩個坐标系的相對取向。歐拉角方法隻是其中的一種。此外,不同的作者會用不同組合的歐拉角來描述,或用不同的名字表示同樣的歐拉角。是以,使用歐拉角前,必須先做好明确的定義。

順規

在經典力學裡,時常用zxz順規來設定歐拉角;照着第二個轉動軸的軸名,簡稱為x順規。另外,還有别種歐拉角組。合法的歐拉角組中,唯一的限制是,任何兩個連續的旋轉,必須繞着不同的轉動軸旋轉。是以,一共有12種順規。例如,y順規,第二個轉動軸是y-軸,時常用在量子力學、核子實體學、粒子實體學。另外,還有一種順規,xyz順規,是用在航空航天工程學;參閱泰特-布萊恩角。

以上兩部分均來自維基百科,我之是以把順規特殊挑選出來,是因為第一段定義中使用的順規與Unity中使用的順規是不一樣的,如果不理順這一點,很容易造成混淆,這會将那些學Unity而檢視歐拉角、萬向節死鎖之類文章的人帶向誤區。

Unity中的定義

以下内容來自Unity文檔中Transform.eulerAngles的定義,本身是一個Vector3,就是一個三維矢量,

分别含有xyz三個參數。

The x, y, and z angles represent a rotation z degrees around the z axis, x degrees around the x axis, and y degrees around the y axis (in that order).

Only use this variable to read and set the angles to absolute values. Don't increment them, as it will fail when the angle exceeds  degrees. Use Transform.Rotate instead.
           

意思就是說:

xyz代表了三個角度,它們定義了一組有序的旋轉,即圍繞z軸旋轉z度,然後圍繞x軸旋轉x度,然後圍繞y軸旋轉y度。
你應該隻去讀取或者直接設定這些數值,不要增加它們,因為當角度超過度将會失敗。應該使用Transform.Rotate去替代執行旋轉操作。
           

解釋

由于Unity内部使用四元數去執行旋轉,不會存儲歐拉角的累計值,是以它說超過360度會失敗是可以了解的,它用四元數執行完運算之後,會更新最後對應的歐拉角數值,而這個結果歐拉角隻是代表了等值的旋轉變化結果,卻無法代表中間過程,由于歐拉角旋轉Z軸361度與1度有一樣的結果,它便最後隻存儲了1度,以便于我們觀察和使用。

Unity中的順規

特别注意的是,上述文檔中說明了Unity使用zxy的順規,這與維基百科定義是不同的,是以不能使用上面那張圖檔中的旋轉來了解。那麼我們嘗試來了解一下,Unity中的歐拉旋轉究竟是如何的。

歐拉旋轉的小實驗

這裡,我使用Unity制作了一個小實驗,可以指定在x+,x-,y+,y-,z+,z-這些軸向來旋轉一個箭頭,見下圖。

【Unity程式設計】Unity中的歐拉旋轉歐拉角的定義
  • RotateXX按鈕代表旋轉指定的軸向XX
  • Applied Angles代表累計的歐拉旋轉角
  • Result Angles代表Unity經過四元數計算之後輸出的結果歐拉角
  • Slider控制旋轉的速度
  • 左邊居中的空白框用來顯示待旋轉的執行項,每次使用RotateXX按鈕都會産生一個旋轉待執行項。
  • Rest用來重置
  • Allow Excute代表是都允許執行目前的待執行項。
  • Once All代表是按順序執行這些可執行項還是所有執行項一起執行。

紅綠藍(RGB)三色分别代表XYZ軸,靠近箭頭的三個軸是局部坐标軸,有圓球的一端代表軸的正向。遠離箭頭的上下左右前後斷開的6個半截軸代表了世界坐标軸。軸的正向與局部坐标相同。這便是一切最初始的狀态,物體的全局坐标和局部坐标下的歐拉角都是(0,0,0),也就是沒有任何旋轉的狀态。

這個小程式可以參見我的Github中的UnityLab/Euler頁面https://andrewfanchina.github.io/UnityLabs/Euler/,自己嘗試一下就可以了解這些按鈕的作用,注意需要使用支援WebGL的浏覽器打開,比如Chrome或者Firefox(360極速浏覽器8.7版本以上貌似也是支援的)。檔案較大,網速慢的稍等一小會兒。

需要注意的是,我在寫小程式的時候,刻意使用了累加計算的歐拉角(小程式中的Applied Angles),也就是說,你所執行的每次旋轉都被累加到一個Vector3中,最後被設定到箭頭的目前歐拉角,相當于箭頭被複位之後,重新執行了最新的歐拉角累計值。舉例來說,累加計算的歐拉角(90,90,90)所對應的結果都是一樣的,無論你是按照什麼順序點選三個旋轉按鈕。這樣,這個小程式可以模拟從初始狀态經曆過目前累計的歐拉旋轉。

歐拉旋轉的旋轉軸

前述一直沒有明确的一個問題,就是我們在圍繞哪一個軸進行旋轉?在Unity的官方文檔上也沒有說清楚這個問題。

準确地說,Unity中每次執行歐拉旋轉,都是使用“目前軸”。

比如在Transform的文檔中:

public void Rotate(Vector3 eulerAngles, Space relativeTo = Space.Self);
           

Description

Applies a rotation of eulerAngles.z degrees around the z axis, eulerAngles.x degrees around the x axis, and eulerAngles.y degrees around the y axis (in that order).

if relativeTo is left out or set to Space.Self the rotation is applied around the transform’s local axes. (The x, y and z axes shown when selecting the object inside the Scene View.) If relativeTo is Space.World the rotation is applied around the world x, y, z axes.

這個函數提供了一個可選的相對空間坐标系參數:

  • Space.Self 局部坐标系,意味着本次歐拉旋轉以物體目前的局部坐标朝向為基礎出發執行旋轉。
  • Space.World 世界坐标系,意味着本次歐拉旋轉以物體目前的世界坐标朝向為基礎出發執行旋轉。

    最重要的倒不是它有可選的世界坐标系,一般而言,常用的旋轉都是相對目前局部坐标系執行的。

    最重要的是:在本次歐拉旋轉過程中,它的相對軸是始終不變的,不變的,不變的…

    比如我們可以指定一組歐拉旋轉(90,60,30),通過前述的順規我們知道,先繞Z軸旋轉30度,再繞X軸旋轉90度,再繞Y軸旋轉60度,雖然有這樣的順序,但是Z旋轉後相對X軸、Y軸,都是執行本組歐拉旋轉前的那個軸向,它沒有發生變動,是以我稱它為“目前軸”。在Unity中的歐拉旋轉就是這樣定義的,不排除其它學術中歐拉旋轉有不一樣的定義方式。是以,執行:

Transform.Rotate(new Vector3(,,))
           

和執行

Transform.Rotate(new Vector3(,,));
Transform.Rotate(new Vector3(,,));
Transform.Rotate(new Vector3(,,));
           

的結果是不一樣的。第一種情況,隻執行了一組歐拉旋轉,第二種情況,執行了三組歐拉旋轉,後兩組歐拉旋轉的相對軸在旋轉時已經發生了變動。

使用小程式驗證旋轉軸

上面的小程式執行的始終隻有一組歐拉旋轉,它每次累計歐拉角變化之後,都相當于從初始狀态重新執行累計歐拉角的旋轉。是以它很容易來驗證相對的旋轉軸向。

假設我們設定一組歐拉旋轉(90,90,90),其最後的旋轉結果朝向如下圖A。

【Unity程式設計】Unity中的歐拉旋轉歐拉角的定義

那麼,我們按照Unity定義的順規,先執行Z軸旋轉90度。

【Unity程式設計】Unity中的歐拉旋轉歐拉角的定義

它的旋轉軸是初始的+z軸,軌迹記錄了本次旋轉劃過的位置。接着是繞X軸旋轉90度。

【Unity程式設計】Unity中的歐拉旋轉歐拉角的定義

它的旋轉軸也是初始的+X軸,接着是繞Y軸旋轉90度。

【Unity程式設計】Unity中的歐拉旋轉歐拉角的定義

它的旋轉軸也是初始的+Y軸。最終我們得到了(90,90,90)這一組歐拉旋轉的最終結果,與圖A的結果相同。可以看出,它的确是沿着初始的固定軸向在進行按Z、X、Y順序的旋轉。

總結

最後,我們總結一下Unity中的歐拉旋轉。它是沿着Z、X、Y順規執行的旋轉,一組歐拉旋轉過程中,相對的軸向不會發生變化。Transform.Rotate(new Vector3(90,60,30)),代表執行了一組歐拉旋轉,它相對的是旋轉前的局部坐标朝向。

正是這種順規和軸向的定義,它導緻了“萬向節死鎖”的自然形成。

且聽下回分解…