世界是3D的,顯示器是2D,將三維空間的物體變換到二維空間,再到最終屏幕上成像的過程,在圖形學中叫做3D渲染流水線。這個過程着實有點複雜了,讓我們慢慢來,本節我們先介紹一些基本的空間變換知識。
這部分知識點非常重要,不管以後做遊戲、仿真項目,還是其他圖形應用,物體的空間變換是必須要理解的,因爲很多實際的需求需要你自己分析,再去實現。就Unity來說吧,它已經把圖形學中的矩陣、向量運算封裝的相當易用了,但是如果你不明白它是怎麼玩的話,你自己的需求還是很難實現。很多人說Unity上手快,易學,確實,拖拖拽拽,加個腳本組件就能控制遊戲對象,但是要想玩得好,就必須要理解相關的圖形學背後的原理。雖然你不用真的把Unity封裝的那些高數實現一邊,但是你要懂,一個函數調用了它會發生什麼。
好了,進入今天的主題。
我們先以2D空間爲例,推導空間變換的矩陣表示,3D空間的推理是一樣的,照葫蘆畫瓢就行,後面我們給出3D空間的變換的矩陣表示。
什麼是矩陣
我們就把它看成一堆神奇的有規律的數即可,不用想的太複雜,它就是個工具。我們不是搞科研,那是數學家的事兒,我們只是應用矩陣這個工具,來解決圖形學的問題。我們沒必要對矩陣研究非常深入,我們只需要理解它如何在圖形學中的應用即可。就像你會開車,不會修車,但這並不會影響你漂移。
記得線性代數書上引入矩陣給了這麼個例子:
這個初中就學過了吧 – 二元一次方程。
這倆方程還能寫成:
對,這就是矩陣乘法。
一個2x2(兩行兩列)的矩陣 乘以 一個2x1的矩陣,得到一個2x1的矩陣。
可以看出:一個 n x n 的矩陣 和 一個 n x m 的矩陣相乘,等於一個 n x m 的矩陣。
問題1
那麼,請問:n x n 的矩陣 和 m x n 的矩陣相乘,等於多少。
答案:不能相乘。不相信你乘給我看看。
好了。現在已經學會了矩陣乘法了。你看,矩陣也不是很難嘛。
問題2
矩陣和向量(或者說點)相乘怎麼計算?
其實點和向量在表示上是沒有區別的(x,y),矩陣是工具,它不會因爲你是點不是向量而改變運算規則,對於運算的結果,在於人爲的解讀。舉個例子,你炒菜加糖還是加鹽,鍋是不會拒絕的,但是對於炒出來的菜,你是要負責任的。我想,你應該懂我意思了吧。
這其實不算問題,上面的就是矩陣和向量相乘呀。
如果有人告訴你(x,y)是個向量,那就默認爲它是從原點到點(x,y)位置的一個向量。總不能上面把它豎着寫,你就不認識了吧。
那個T叫做轉置符號,就是橫着寫和豎着寫的區別。向量還是原來的向量,只是後來有錢了,走路都橫着走了,但還是原來那個向量。
現在我們知道,向量其實也可以看作是一種特殊的矩陣。
問題3
此時,你可能會問,爲什麼要轉置啊,不轉置不行嘛?
行!當然行。
在我看來,轉不轉置都一樣。都是形式而已。中國人這還用問?
不轉置的話,上面的矩陣,就得這麼寫:
1x2 的矩陣 乘以 2x2 的矩陣,得到 1x2 的矩陣。 沒毛病。
我們始終要記住,我們最終關心的是最後得到的操作數,怎麼解釋它是人乾的事,或者說你乾的事,和你橫着寫豎着寫有關係嘛?只要你遵循對應的運算法則就是咯。你不要問爲啥要有轉置,存在即合理,就像學校查衛生時,垃圾桶不準放垃圾一樣,誰知道那些人咋想的。
問題4
我想看看兩個“真”矩陣相乘。
其實,你仔細品下,你自己也能寫出來。
下面,我們來看看矩陣是如何表示變換的。
常見的變換就是:平移、旋轉、縮放。像其他錯切變換、對稱變換啥的,哎,怎麼說呢,你平時應該用到不到,用到了再學吧。道理都是相通的嘛,學會了基本的,其他的照葫蘆畫瓢唄。
平移變換
話說,空間中某個物體,算了,來張我的照片吧。
把我的照片從一個地方挪到另一個地方,需要幾步?咳咳,你先別管爲什麼移我的照片。
很簡單,數學上,就是把每個點都平移一下。
假設(x,y)爲圖像上某一點,移動另一個位置(x’,y’),假設就位移了(a,b)吧,意思就是x方向位移了a,y方向位移了b。
那麼有:
x' = x + a
y' = y + b
好了。現在你看能不能寫成矩陣的形式。
是不是寫不出來啊~~~
先放一放啊,我們後面再說。
旋轉變換
現在把我的照片轉一轉。算了,換張你們喜歡看的吧。
現在我們規定逆時針旋轉爲正。你要規定順時針旋轉爲正。也行。前面都說了,關鍵看人(你)怎麼解釋。
我們現在取圖像上任一點A(x,y),旋轉beta角度之後,到達A’
A點原本與X軸的夾角爲alpha,爲了方便,設: |OA|=|OA’|=r
則我們可以列出以下方程:
整理下:
現在,你把這個寫成矩陣乘法的形式看看。
是不是很簡單。
縮放變換
這個很簡單。
A(x,y) 放大 Sx 和 Sy倍,得到 A’(x’, y’)
x' = x * Sx
y' = y * Sy
寫成矩陣的形式:
齊次座標
好了。基本變換到此結束。
此時,你是不是發現,除了平移變換,旋轉和縮放變換都能寫成矩陣的形式。別人有的,它當然也想要啊。
所以,引入了齊次座標。
不知道誰起的這個名字,想象力豐富的我,打死我也想不出來它乾的事和它的名字有啥關係。
簡單說,就是給向量或者矩陣多加一個維度,方便將基本的變換(平移、旋轉、縮放)都可以用矩陣乘法的形式表示。
其實,我們知道,就是爲了照顧平移變換。
座標變換的齊次座標表示
那麼如何表示呢?
我們先將之前的三個變換用齊次座標表示的矩陣形式寫出來:
平移變換:
旋轉變換:
縮放變換:
大家快速驗算一下,應該都成立吧。
通過觀察可知,我們將點(x,y) 變成了齊次座標(x,y,1).
有時候給你一個(x,y),你知道它是點還是向量?因爲大家都知道,向量的代數表示和點是一樣的。
這個時候就是齊次座標的第二個妙用。
點(x,y) 寫成齊次座標是: (x,y,1)
向量(x,y) 寫成齊次座標是: (x,y,0)
爲啥?
我也不知道爲啥,我就覺得這麼幹是對的,而且很神奇。
咱們來驗算一下:點是有位置的,向量是沒有位置的,是吧。
那咱們用平移矩陣來驗算一下不就好了嘛。
數學是不是很奇妙。
那這個有啥用呢?
後面推導3D渲染流水線的投影變換階段好像用到,到時候再說吧。
其實,一個點或者向量的齊次表示不是唯一的,點(x,y)可以表示成(kx,ky,k),向量(x,y)可以表示成(kx,ky,0). 這個不難理解吧。
三維空間變換
三維空間變換矩陣的推導和二維空間很相似,只不過多了一個維度嘛。
三維座標系,有2種,左手座標系和右手座標系,區別就在於Z軸的朝向不同。爲啥有2種。額,我想這個和人有左撇子和右撇子道理應該差不多吧。
那麼,我們用那種呢?前面已經說了,隨便你,只要你選定一種座標系,數學推導的過程是完全一樣的。我們這裏推導使用的是右手座標系,因爲我是右撇子。
平移矩陣,幾乎和二維變化一樣:
旋轉矩陣,二維空間繞的是原點,三維空間繞的是座標軸。
繞z軸旋轉某個角度的旋轉矩陣:
大家是不是有點眼熟?對嗎,這不就是二維旋轉矩陣加了一個維度嘛。大家想想,繞z軸旋轉,是不是在XY平面內?那是不是就是二維旋轉?這不就得了,那繞x軸和繞y軸旋轉,不就是一樣的道理嘛?
繞x軸旋轉某角度:
繞y軸旋轉某角度:
有個事情要注意下啊,我們這裏推導的變換矩陣和你們在其他地方看到的可能不一樣,彆着急。你轉置一下看看是不是就一樣的了。這是最後一遍嘮叨了啊,轉不轉置,你自己看着辦,你喜歡用哪種就用哪種,關鍵要用對場合。我的建議是,根據你以後學習的圖形編程API來決定使用那種風格的矩陣、座標系。比如說,OpenGL,使用的是右手座標系,使用的是向量右乘(矩陣x向量, 而不是向量x矩陣,前者是列向量,就是豎着寫的那個,後者是橫向量,即使橫着寫的那個)。所以,我就是按着這個推導的,並不是因爲我是右撇子。
本節介紹的基本變換,經過組合,可以形成複雜的變換。
怎麼組合?
多個變換矩陣相乘呀,注意,相乘的順序喲。這個現在你是感受不到的,只有你實戰的時候,你才能發現區別。
好了,本節的內容到此就結束了。
小夥伴們有什麼建議,可以留言。我也不知道是不是講得太細,或者講得太粗,就按照自己的想法來。大家有什麼建議可以說出來。或者如果大家有感興趣的話題,也可以說出來。只要我會的,我也會寫相關的教程。其實,我也不是科班出身,只是興趣使然,歡迎小夥伴一起探討學習。
歡迎大家關注我的公衆號【OpenGL編程】,定期分享OpenGL相關的3D編程教程、算法、小項目。歡迎大家一起交流。