(連載)邊喝咖啡邊學Unity——第二章 預備知識體系(2),邊學unity
什麼是矩陣?
mn個數排成m行n列形成的矩形表稱作一個mxn矩陣。
行向量與列向量
由於向量我們可以看做是一種特殊的矩陣,即只有一行或者只有一列的特殊矩陣。因此我們把只有一行的向量稱為行向量,只有一列的向量稱為列向量。
注意我們的程式在儲存矩陣的時候其實是將mn個數存放在一個線性表中,因此擷取一個矩陣中的第幾行第幾列的元素有一個優先原則。這裡特別提出,Unity在Matrix4x4這個類的手冊中有這樣一句話:
Matrices in unity are column major. Data is accessed as: row+ (column*4). Matrices can be indexed like 2D arrays but in an expression likemat[a, b], a refers to the row index, while b refers to the column index (notethat this is the opposite way round to Cartesian coordinates).
Unity中的矩陣使用的是列優先。資料通過行+(列*4)的方式擷取。矩陣可以被索引為類似二維數組的形式,但是要以mat[a,b]這樣的運算式。其中a代表行號,b代表列號(注意這正好與笛卡爾座標系相反)。
行向量與列向量的數學表達:
零矩陣
所有元素都是0的矩陣。
單位矩陣
形式如下的矩陣稱為單位矩陣
其特點明顯:所有元素中行號與列號相等處元素為1,否則為0,也就是從左上至右下的一條對角線為1,其他元素都為0的矩陣。關於單位矩陣的特殊性後面會提到。
矩陣相等
維數相同且所有元素相等的兩個矩陣我們認為是兩個相等的矩陣。
矩陣加法、矩陣減法、矩陣數乘
這3個運算都比較簡單,因此寫在一起。他們分別表示對應的行列上的數相加、相減得到的矩陣。一個數與矩陣相乘則是所有元素都乘以這個數得到的矩陣。
轉置矩陣
矩陣乘法
矩陣乘法比較特殊,我們在讓矩陣相乘的時候首先保證這個過程有意義。有意義的前提的矩陣乘法的左乘矩陣的行數與右乘矩陣的列數相等。
數學過程:矩陣a的列數與矩陣b的行數相等,那麼相乘得到的矩陣c中的第i行j列的元素
由於只需要a的列數與b的行數相等,因此我們可以想象一個4元行向量有4列,那麼他可以左乘一個4x4矩陣。一個4元列向量有4行,因此他可以右乘一個4x4矩陣。由於行向量與列向量看起來只是書寫形式上的區別,實際上行向量與列向量存在轉置關係。
矩陣乘法不滿足交換律,但是在乘法有意義的情況下有:
在矩陣運算中,向量與矩陣相乘分為兩種:行向量左乘與列向量右乘。
行向量左乘與列向量右成在本質上表達的東西沒有太大差別,只是由於行向量與列向量存在轉置關係,各個遊戲引擎有他的書寫習慣,以及美感偏好。DirectX中使用的是行向量左乘,而OpenGL中使用的列向量右乘。
逆矩陣
一個矩陣有逆矩陣首先需要滿足一個條件:該矩陣為非奇異矩陣或滿秩矩陣,又或者說該矩陣的行列式≠0。
滿足該條件的矩陣a存在一個逆矩陣b,使得ab=單位矩陣I。
我們記作
空間變換
瞭解座標系、向量、矩陣之後,我們可以開始瞭解空間變換了,而這部分知識基本上覆蓋了目前絕大部分的與圖形映像開發有關的領域。
前面我們對向量矩陣的定義、特性以及運演算法則作了初步瞭解,然而線性代數沒有告訴我們向量、矩陣運算的幾何意義。而本書將帶領讀者去探索向量矩陣運算的空間幾何意義。
Matrices can perform any kind ofliner transform.
一般來講矩陣能描述任意的線性變換。滿足兩點的空間變換我們稱之為線性變換:原點不發生移動;線段和直線之間的平行關係保持不變。換句話說,線性變換可能展開座標系,但是不會扭曲座標系。
對於任何圖形的旋轉(rotation)與縮放(scaling),屬於線性變換:線段間的平行關係沒有發生變化、原點沒有發生移動。而平移(translation)變換則不屬於線性變換,無論變換前以哪個點為座標系原點,變換後的原點一定發生了移動。
因此我們說線性變換是一種可能改變線段的長度、線段間的夾角、圖形的面積、體積的變換。
現在我們解開心中的疑問:矩陣是如何完成變換的?nxn個數字如何讓我們的座標系翻天覆地?
我們先從二維空間中展開想象。
假設我們有一個圖片在平面直角座標系中,其4個頂點分別為(0,0),(1,0),(0,1)(1,1)。
然後假設有這樣一個2x2矩陣:
我們以這樣的2x2矩陣對這個圖形進行變換,意味著什麼呢?
其實這個矩陣可以看作2個行向量p=[2,1],q=[-1,2]。我們在平面直角座標系中畫出這2個向量:
其實這個2x2矩陣代表的意思是將原x軸變換至p向量坐在的方向;將原y軸變換至q向量所在的方向。由於座標系原點沒有發生移動,因此這是一個線性變換。實質上這是一個旋轉變換,我們可以伸出我們的左手,大拇指指向x,食指指向y,然後想象將任意一個形狀放在兩個手指間,然後大拇指由原方向變換至p方向,食指由原方向變換至q方像,這個過程就是我們這個矩陣所描述之變換過程。原映像逆時針旋轉了26°。
-->
不僅僅是旋轉,我們的圖形的邊長由原先的1,變換後變為了。因此這個矩陣還對我們的圖形進行了展開。
這個例子中我們的p與q恰好垂直切長度相等,於是構成了一個正方形,實際上更普通的情況他們構成的是一個平行四邊形。這個平行四邊形我們叫做“偏轉盒”。二維偏轉盒我們可以使用兩個手指來比劃,三維偏轉盒我們可以使用3個手指來比劃,因此讀者試著自己想象3x3矩陣中的線性變換的偏轉盒子。
我們再回頭看我們對於線性變換的定義:線段間的平行關係不發生改變;原點不發生移動。可見2x2矩陣與3x3矩陣分別真好可以表達2D與3D空間中的線性變換。
仔細觀察上述變換,假設我們把右邊的紙旋轉讓映像重新“回正”,我們順時針對紙張旋轉了26°,於是我們拋出一個概念:
我們對一個圖形或者物體按照某個角度旋轉,相當於對這個座標系按照這個角度的負值旋轉。
看到這裡其實我們可以看到一個非常非常非常明顯的規律,也就是變換矩陣中能出現cos的地方必然是在行列號相等的地方(一條對角線),任何行列號不相等的地方都可能出現sin。
縮放矩陣中的3個係數分別對應著對原映像的3個軸向的縮放程度。
一種特殊的線性變換我們稱為鏡像。例如2維變換矩陣 所代表的變換,其變換後的映像如同鏡子一樣翻折出現在y軸的另一邊。
切變變換
講完線性變換的最後我們提一下切變變換,這是一種特殊的變換,其變換過程為“扭曲”座標系。其過程希望讀者自行查閱相關資料。
至此我們研究完了線性變換的矩陣表達與幾何意義。在空間數學中,我們認為座標與矩陣的乘法就等於變換本身。
那麼讀者心中是否有疑問,平移變換不屬於線性變換,那麼能否用矩陣來表達呢?
答案是可以,但是有一個條件:n維空間中的平移變換無法用nxn或者更小的矩陣來表達,至少需要(n+1)x(n+1)的矩陣來表達。我們來看看為什嗎?
對於2D 3D中的線性變換矩陣,我們將其拆分為與座標軸數量相當的向量基p,q/p,q,r。我們可以理解為線性變換的過程是將對應的座標軸上的單位向量變換至該矩陣表示的向量基的過程。其過程必定不包含平移。
那麼我們怎麼才能表達平移?
我們前面講了座標與矩陣的乘法等於變換本身,因此我們假定2個座標(1,1)(2,2)構成的線段,將其書寫為2個行向量[1 1][2 2],將其左乘一個2x2單位矩陣(我們認為2x2單位矩陣是一個x,y方向上縮放係數都為1的縮放矩陣)完成線性變換:
[1 1] 變換後的結果仍然為[1 1],[2 2]同理。得到的線段(圖形)沒有發生任何變畫
我們對這個變換矩陣進行任意修改:將[1 1]對角線上的值進行修改,發現變換過程產生了等比縮放。將沒有在這條對角線上的值進行修改,發現變換過程產生了旋轉。
除此之外這個2x2矩陣上面已經沒有其他地方可以讓我們修改能夠產生平移變換的可能性了。於是我們不得不提出一個大膽的設想:我們能否得到一個包含有更多數的矩陣來完成變換,使該矩陣能有地方讓我們修改其值使原圖發生非線性變換?
答案是肯定的,在認知的過程中思維的變遷與升華是必不可少了。於是天才一般的想法被提出來:我們以3維矩陣、向量來表達2維空間中的變換和點。
我們來看對於點[1 1]擴充一個維度之後[1 1 1],我們對擴充過後的這個點座標給予以下要求:
這不是一個3維空間中的點,它仍然是2維空間中[1 1]這個點。第三個維度我們稱之為w。我們想象不出來這個[1 1 1]的點其意義為何的時候,我們只能看到這個點在2維空間中的影子(投影),我們由這個影子反過來來想象它。對於點[1 1 1],[2 2 2],[3 3 3],他們投影在二維空間中都是[1 1]這個點,投影的過程即將前面2維分別除以第三個維度w。當w=0時,除法無意義,因此我們認為點[x,y,0]在二維空間中的意義為無窮遠的點。
這個空間我們稱這個2維空間的3維齊次空間。這個[x,y,w]形式的座標,我們稱其為改2維座標的3維齊次座標。同樣的,我們的2維單位矩陣,當w=1時,對應著一個3維齊次單位矩陣
我們再次以齊次行向量與齊次單位矩陣的左乘表達變換:
[x y w] ,此時左矩陣列數與右矩陣行數仍然相等,乘法仍然有意義。
我們再觀察,齊次矩陣中是否有地方可以讓我們的座標系產生平移的可能?
齊次矩陣中原2維矩陣的資料項目我們不必嘗試了,我們試著修改多出來的abcd。
[x y w]=[x+a,y+b,w ] (結果矩陣中:)
不可思議的事情發生了,由於我們對於齊次空間的定義,任意點[x,y,w],x/w , y/w相等的兩個點我們認為是同一個點。於是我們可以看到這個變換矩陣令原座標系中的任意點[x y]變換後都成為了[x+a y+b]!也就是任何圖形、線段都沿x方向平移了a,沿y方向平移了b。
結論:對於n維空間中的圖形(座標系),nxn矩陣可以表達線性變換,引入n+1維齊次空間,即可用n+1維齊次矩陣乘法來表達平移變換。而原線性變換,依然可以在該齊次矩陣中的原對應位置表達線性變換。
於是齊次矩陣將線性變換與平移變換統一到了一起。我們將線性變換與平移變換都發生的變換叫做仿射變換。
換句話講,一個nxn矩陣,可以表達任意的n-1維空間中的仿射變換。
由此我們得到三維空間中的平移矩陣,顯然是一個3+1=4維的矩陣:
我們再回到幾何層面上,我們認為對位置有意義的量,進行平移才有意義,本身沒有位置概念的量,我們進行平移沒有意義。最典型的就是點和向量。雖然我們前面說向量可以由終點來表達,但是狹義上的點是有位置的,而向量只有大小和方向。因此我們不能對一個狹義上的向量進行平移變換。
透視投影變換與投影矩陣
我們簡單的提一下投影變換。投影變換通常發生在將一個3維空間中的物體投影至一個平面上(陽光將人照射出來的影子投影在牆上)。而我們表達一個3維平面最簡單的3個平面分別是x=d,y=d,z=d。他們各自是與圓點距離d處的平面。
我們假設要將一個三維圖形投影至z=d這個平面,那麼任意的齊次點[x y z]投影至z=d平面上,得到的點座標應為:[x y z ]/(z/d)=[x y d]將z消除得到了2維的空間(圖形). 因此我們構造一個w為1的齊次空間,另[x y z 1]投影之後變為[x y z z/d],那麼矩陣構造出來為:
任意點[x y z 1]與該矩陣相乘得到[x y z z/d],[x y z z/d]回到原空間則為[x y d],該矩陣就是三維空間中向z=d平面投影的投影矩陣。
方位與角位移
在掌握了向量、矩陣的一些基本知識之後,我們問自己一個問題:
一個向量有沒有角位移?一個物體有沒有角位移?
我們知道向量只有大小和方向,即使你要強迫一個向量繞自己旋轉,旋轉後向量仍然大小和方向沒有改變,我們認為向量還是那個向量。但是如果說我們讓一個物體繞自己旋轉,比如把一個人翻過來,我們還認為是一樣嗎?由於地球引力的關係,被倒過來的人能夠明顯感覺到自己的狀態發生了改變。而這種改變不單單是我們把一個人從某個地方弄到另外一個地方這種位移。在物理上我們稱這個人發生了角位移。
我們將旋轉的程度稱為角位移。位移往往用來表達物體的位置,角位移往往用來表達物體的方位。
我們知道了n維空間中的位移可以用n個數字來表示。例如2維空間中的點朝x移動了5,朝y移動了10,那麼用5,10 兩個數字即可表達這個位移。
那麼3維空間中的角位移又如何來表達呢?
矩陣表達角位移
我們可以構造一個3維的旋轉變換矩陣來表達3維空間中的角位移(這種矩陣我們前面已經給出)。這種表達方式非常地直接,但是並不簡潔。我們瞭解下矩陣表達角位移的優缺點
優點:立即性;更適合底層圖形API直接使用;多個角位移串連時只需要連續相乘即可得到最後的角位移;只需要簡單的將這個矩陣轉置即可求得這個角位移的逆;
缺點:佔用更多的空間(3維空間需要9個數來表達角位移);晦澀難懂(我想讀者應該已經有所體會);有冗餘資料、有可能發生矩陣蠕變。
歐拉角(Euler Angle)表達角位移
神馬?我們單純的描述一個物體旋轉的情況,竟然要搬出一個3x3矩陣?我們不能忍!於是乎本章開頭提到的歐拉角登場了。歐拉角由著名的數學家歐拉(Euler)提出,並且以他的名字命名。我們看看歐拉角是個什麼角,歐拉角有什麼厲害之處:
歐拉角將一個三維物體的旋轉分解為分別繞3個兩兩垂直的軸線的旋轉。至於哪3個軸,按照什麼順序分解,歐拉沒有告訴我們,但是最有意義的情況是我們按3維座標系的3個軸按照一定的順序將旋轉分解。這個順序有很多種約定,我們這裡提一下”heading-pitch-bank”約定。
即在左手座標系中,我們繼續伸出左手,3個手指兩兩垂直,得到我們前面提到的左手座標系,食指指向即y+方向的旋轉量我們稱為heading,中指指向的z+方向的旋轉量我們稱為bank,大拇指指向的z+方向的旋轉量我們成為pitch。
由此我們將一個物體任意的旋轉分解為3個旋轉量,分別得到3個角度。我們可以由3個數組成的序列來描述近乎所有的旋轉!但是歐拉角仍然無法表達所有的旋轉。最著名的就是萬向節死結現象。即當pitch=+-90°時heading與bank都豎直從而減少了一個旋轉度,此問題至今沒有什麼好的辦法解決。
優點:容易使用;佔用的數最少;簡潔;所有的歐拉角表達出來的角位移都是合法的。
缺點:不唯一性(360°和0°雖然數值變化了但是表達的方位沒變);最難插值(很難在兩個歐拉角中間進行插值,這對我們加入動畫非常惱火);永珍鎖問題。
接下來我們認識最後一種表達角位移的方式:
四元數(Quaternion)表達角位移
關於四元數是什麼留給讀者自行延伸閱讀,這裡只提出四元數實質上是對複數(a,b,c,d)的一種幾何解釋。
複數(a,b,c,d)有一個實部a,3個虛部分別為b,c,d。複數可以進行加、減、點乘、叉乘,可以求它的共軛複數,也可以計算複數的模和求逆。
四元數如何表達角位移呢,我們讓n為旋轉軸方向的單位向量,,那麼四元數q=[]
表達了這個角位移。其中nx ny nz分別是旋轉軸在軸線上的投影向量。
我們特別提出四元數的插值(slerp)。四元數的插值過程我們只需要知道有一個開始狀態q,結束狀態p,插值參數t(0<=t<=1),那麼slerp函數可以非常快速平滑地在q與p兩個狀態中插入一個旋轉狀態。其數學、幾何解釋也請讀者自行延伸閱讀。
優點:slerp提供了2個方位間最為平滑的插值;角位移的串連和求逆更為迅速(相比矩陣);每個角位移佔用4個數的記憶體開銷,中等開銷;
缺點:最難於使用(光理解它就已經比較科幻了);可能存在一組四元數表達出不合法的角位移。
我們瞭解了角位移的3種表達方式,具體在開發中使用哪一種來表達需要理解他們各自的優越點,根據實際情況選擇。而這3種方式表達的角位移可以進行互相轉換,其數學過程較為複雜,我們可以直接調用現有的API來完成。
幾何圖元
明天繼續….