本文發佈於遊戲程序員劉宇的我的博客,長期更新,轉載請註明源地址https://www.cnblogs.com/xiaohutu/p/10979936.htmlhtml
數學,是人類對客觀世界中數量關係和空間形式本質特徵進行研究的科學。對一樣的某一特徵或者關係,能夠根據需求用不一樣的數學符號、定義和過程來表達。在遊戲引擎中,咱們也有不少這樣的例子,好比本文說到的旋轉。程序員
旋轉是一個過程,一個物體圍繞周或者點角度變化的過程。爲了描述這個過程咱們必須有參照物,因而咱們先定義一個世界座標系,笛卡爾座標系。spa
歐拉角用(x, y, z) 分別來表示這個物體相對三個座標系的夾角,這是由數學家歐拉首先提出而得名的。 .net
然而僅僅有(x, y, z) 來表示旋轉是不夠的,還有兩個因素:3d
首先是旋轉順序,從各個軸上進行角度旋轉時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)。 code
其次是旋轉的參照座標系,歐拉角按旋轉的座標系分爲:
內旋(intrinsic rotation)即按照物體自己的座標系進行旋轉,座標系會跟隨旋轉與世界座標系產生偏移。
外旋(extrinsic rotation)即根據世界座標系進行旋轉。
這是咱們看看Unity3d中Transform的Rotate,最後一個參數即座標系: orm
public void Rotate(Vector3 eulerAngles, Space relativeTo = Space.Self);
注意:Unity3d使用的是zxy的順規,且進行一次歐拉旋轉的zxy依次執行過程當中,相對軸始終是運算開始以前的軸向。xml
注意咱們前面提到的旋轉的規則 [ 進行一次歐拉旋轉的zxy依次執行過程當中,相對軸始終是運算開始以前的軸向 ]htm
在這個規則下,zxy順規時,若是x的旋轉爲90度,那麼任意的(z,90,y)都與(z-y,90,0)獲得的結果都是相同的,此時咱們稱z軸失去了自由度,並稱這種狀況爲萬向節死鎖。之因此叫Gimbal Lock,是由於這種狀況正好和Gimbal,即陀螺儀的狀況是完美匹配的,死鎖狀況也相同。具體狀況這篇文章寫的很好,你們能夠看一下:https://blog.csdn.net/andrewfan/article/details/60981437blog
這種狀況下,物體的某一個旋轉,會有多個歐拉角數值與其多對一的對應,那麼對歐拉角進行插值是沒有意義的。若是隻旋轉一個軸,其餘軸不動,那麼直接設置歐拉角的數值卻是沒有什麼問題。
同時咱們也不可貴出死鎖的緣由並非由順規決定的,而是因爲歐拉角的原理和計算方式,一個軸的旋轉必然帶來另外軸的旋轉所致使的。
這時候,又有了一個思路,咱們可使用基於一個單位矢量來表示方向,再用一個標量來定義繞這個矢量來旋轉多少角度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。能夠看出q逆時針旋轉了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個浮點數就來表達一個旋轉過程,而且能夠方便簡單的快速計算旋轉的疊加。這對於遊戲引擎來講是很是有意義的,能夠加快運算速度。
四元數還有不少具體的特性,計算規則等,感興趣的能夠去研究,本文主要討論旋轉,這裏再也不贅訴。
【球形插值】
四元數還能夠實現球形插值,制定兩個旋轉qa到qb,時間間隔爲t,那麼此刻的旋轉插值爲:
其中θ爲兩個旋轉之間的夾角:
球面插值能夠在遊戲裏實現很平滑的轉向和球面運動。
已知歐拉角,求四元數:
已知四元素,求歐拉角
學過矩陣乘法咱們都知道,若是把向量當作一個列矩陣,那麼與向量維度相同的列數的矩陣乘以它,獲得的結果也是一個列矩陣,即:
因此能夠充分利用左邊矩陣的內容,對右邊的向量進行各類變換(包括平移,縮放,旋轉等等),這裏咱們只討論旋轉。
具體推導過程參考這裏:https://www.cnblogs.com/xpvincent/archive/2013/02/15/2912836.html
假設一個向量V(Vx,Vy, Vz) 繞另一個軸角(nx, ny, nz, θ)進行旋轉,那麼旋轉結果V'是:
這個公式咱們稱之爲羅德里格旋轉公式(Rodrigues' rotation formula),用矩陣計算旋轉在遊戲圖形渲染裏很是廣泛,不過引擎裏通常爲了兼容更多的向量變換,都使用了其次矩陣,即多一個維度的矩陣,本文不表。
總結完這幾種在常見的表達旋轉的數學方式,能夠看到遊戲引擎裏也都使用到這些表達方法,感受收益匪淺,後面準備討論一下齊次矩陣和矩陣變換的原理。