旋轉的數學表達:歐拉角、軸向角、四元數與矩陣

韩国快乐8开奖结果查询 www.wcdoq.com   本文發布于游戲程序員劉宇的個人博客,長期更新,轉載請注明源地址//www.wcdoq.com/xiaohutu/p/10979936.html

  數學,是人類對客觀世界中數量關系和空間形式本質特征進行研究的科學。對同樣的某一特征或者關系,可以根據需求用不同的數學符號、定義和過程來表達。在游戲引擎中,我們也有很多這樣的例子,比如本文說到的旋轉。

歐拉角

  旋轉是一個過程,一個物體圍繞周或者點角度變化的過程。為了描述這個過程我們必須有參照物,于是我們先定義一個世界坐標系,笛卡爾坐標系。

      

  歐拉角用(x, y, z) 分別來表示這個物體相對三個坐標系的夾角,這是由數學家歐拉首先提出而得名的?!?/p>

          

  然而僅僅有(x, y, z) 來表示旋轉是不夠的,還有兩個因素:


  首先是旋轉順序,從各個軸上進行角度旋轉時xyz先后的不同會得到不同的結果。我們稱這個順序定義為順規,下面一段是維基百科的定義:
  在經典力學里,時常用zxz順規來設定歐拉角;照著第二個轉動軸的軸名,簡稱為x順規。另外,還有別的歐拉角組。合法的歐拉角組中,唯一的限制是,任何兩個連續的旋轉,必須繞著不同的轉動軸旋轉。因此,一共有12種順規。例如,y順規,第二個轉動軸是y-軸,時常用在量子力學、核子物理學、粒子物理學。另外,還有一種順規,xyz順規,是用在航空航天工程學;
  按(z-x-z, x-y-x, y-z-y, z-y-z, x-z-x, y-x-y)軸序列旋轉,即第一個旋轉軸和最后一個旋轉軸相同,我們稱之為經典歐拉角(Proper Euler Angle)。
  按(x-y-z, y-z-x, z-x-y, x-z-y, z-y-x, y-x-z)軸序列旋轉,即三個不同的軸,我們稱之為泰特布萊恩角(Tait–Bryan angles)。


  其次是旋轉的參照坐標系,歐拉角按旋轉的坐標系分為:
  內旋(intrinsic rotation)即按照物體本身的坐標系進行旋轉,坐標系會跟隨旋轉與世界坐標系產生偏移。
  外旋(extrinsic rotation)即根據世界坐標系進行旋轉。
  這是我們看看Unity3d中Transform的Rotate,最后一個參數即坐標系:  

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

   注意:Unity3d使用的是zxy的順規,且進行一次歐拉旋轉的zxy依次執行過程中,相對軸始終是運算開始之前的軸向。

 萬向節死鎖(Gimbal Lock)

  注意我們前面提到的旋轉的規則 [ 進行一次歐拉旋轉的zxy依次執行過程中,相對軸始終是運算開始之前的軸向 ]

  在這個規則下,zxy順規時,如果x的旋轉為90度,那么任意的(z,90,y)都與(z-y,90,0)得到的結果都是相同的,此時我們稱z軸失去了自由度,并稱這種情況為萬向節死鎖。之所以叫Gimbal Lock,是因為這種情況正好和Gimbal,即陀螺儀的情況是完美匹配的,死鎖情況也相同。具體情況這篇文章寫的很好,大家可以看一下:https://blog.csdn.net/andrewfan/article/details/60981437

             

  這種情況下,物體的某一個旋轉,會有多個歐拉角數值與其多對一的對應,那么對歐拉角進行插值是沒有意義的。如果只旋轉一個軸,其他軸不動,那么直接設置歐拉角的數值倒是沒有什么問題。

  同時我們也不難得出死鎖的原因并不是由順規決定的,而是由于歐拉角的原理和計算方式,一個軸的旋轉必然帶來另外軸的旋轉所導致的。

 軸向角

  這時候,又有了一個思路,我們可以使用基于一個單位矢量來表示方向,再用一個標量來定義繞這個矢量來旋轉多少角度theta。

  即[x,y,z,theta],前面的xyz表示這個矢量,最后一個表示角度。這個表示方法很簡潔明了,容易理解。

      

  缺點有兩個:

  1.不同的軸角之間不能進行簡單的插值。

  2.不好基于一個矢量加上一個軸角形式的旋轉來進行運算。

  為了解決這些缺點,先人們又發明了使用四元數和矩陣來表達旋轉。

四元數

  這里先說一下基本原理:

  參考鏈接:https://www.3dgep.com/understanding-quaternions/

  譯文:https://blog.csdn.net/lhs322/article/details/80066960

  四元數的概念是由愛爾蘭數學家漢密爾頓發明的,他當時正和老婆一起前往愛爾蘭皇家研究院,一邊走一邊想,路過一座橋時,他頓悟了公式,并立刻把它刻在橋上的石頭上:

     

   那么,為什么四元數能表示三維空間的旋轉呢?首先學過高數我們都知道復數的定義以及幾何意義,復數可以映射到復數平面上,并且對這復數乘以i,得到的復數就相當于復數空間里旋轉了90度。

   例如下圖,p = 2 + i,乘以i后: q = pi = (2+i)*i = 2i + i*i = 2i - 1 = -1 + 2i??梢鑰闖鰍逆時針旋轉了90度。同理乘以-i即為正時針旋轉90度。

      

  此時將復數的虛部擴展為三個,并根據漢密爾頓的著名表達式以及推論

   

  

  四元數的定義可以用來表達笛卡爾坐標系的旋轉,其中i,j,k分別代表笛卡爾坐標系里xyz三個軸的單位向量。這些表達式里 ij = k 是不是很眼熟?兩個互相垂直的單位向量的叉乘等于垂直于兩個向量的單位向量。

   

   經過一系列的推導和運算(略),大家感興趣可以看上面的鏈接,假設一個旋轉的基準向量是(A,B,C),角度是 θ (theta),那么表達這個旋轉過程的四元數如下:  

  

  注意ABC本質上就是基準向量在3個笛卡爾軸上的分量,用來準確描述向量,上面的公式也就是

四元數 q = [cos(θ/2), sin(θ/2)* v‘]; 
即:
w = cos(θ/2) x = A * sin(θ/2) y = B * sin(θ/2) z = C * sin(θ/2)

  假設有一個向量v要進行旋轉,這個旋轉描述為q,那么結果是 v' = qvq-1, 如果要進行多次旋轉,則表示為:

  

  四元數的乘法是:

q1 * q2 =
(w1*w2 - x1*x2 - y1*y2 - z1*z2) +
(w1*x2 + x1*w2 + y1*z2 - z1*y2) i +
(w1*y2 - x1*z2 + y1*w2 + z1*x2) j +
(w1*z2 + x1*y2 - y1*x2 + z1*w2) k

  可以看到,通過一系列的數學推導和定義,可以只用4個浮點數就來表達一個旋轉過程,并且可以方便簡單的快速計算旋轉的疊加。這對于游戲引擎來說是非常有意義的,可以加快運算速度。

  四元數還有很多具體的特性,計算規則等,感興趣的可以去研究,本文主要討論旋轉,這里不再贅訴?! ?/p>

  【球形插值】

  四元數還可以實現球形插值,制定兩個旋轉qa到qb,時間間隔為t,那么此刻的旋轉插值為:

  

  其中θ為兩個旋轉之間的夾角:

  

  球面插值可以在游戲里實現很平滑的轉向和球面運動。

四元數與歐拉角之間的轉換

  已知歐拉角,求四元數:

  

  已知四元素,求歐拉角

  

用矩陣來計算旋轉

   學過矩陣乘法我們都知道,如果把向量看成一個列矩陣,那么與向量維度相同的列數的矩陣乘以它,得到的結果也是一個列矩陣,即:

  

  所以可以充分利用左邊矩陣的內容,對右邊的向量進行各種變換(包括平移,縮放,旋轉等等),這里我們只討論旋轉。

  具體推導過程參考這里://www.wcdoq.com/xpvincent/archive/2013/02/15/2912836.html

  假設一個向量V(Vx,Vy, Vz) 繞另外一個軸角(nx, ny, nz, θ)進行旋轉,那么旋轉結果V'是:

  

  這個公式我們稱之為羅德里格旋轉公式(Rodrigues' rotation formula),用矩陣計算旋轉在游戲圖形渲染里非常普遍,不過引擎里一般為了兼容更多的向量變換,都使用了其次矩陣,即多一個維度的矩陣,本文不表。

 小結

  總結完這幾種在常見的表達旋轉的數學方式,可以看到游戲引擎里也都使用到這些表達方法,感覺收益匪淺,后面準備討論一下齊次矩陣和矩陣變換的原理。

posted @ 2019-06-06 16:13 游戲程序員劉宇 閱讀(...) 評論(...) 編輯 收藏