渲染遊戲的過程能夠理解成是把一個個頂點通過層層處理最終轉化到屏幕上的過程,本文就旨在說明,頂點是通過了哪些座標空間後,最終被畫在了咱們的屏幕上。spa
首先,咱們來看一個簡單的問題:當給定一個座標空間以及其中一點(a, b, c)時,咱們是如何知道該點的位置的呢?3d
座標空間的變換就蘊含在上面的4個步驟中。如今,咱們已知座標空間C的3個座標軸在座標空間P下的表示Xc, Yc, Zc,以及其原點位置Oc。當給定座標空間C中的一點Ac = (a, b, c),咱們一樣能夠依照上面4個步驟來肯定其在座標空間P下的位置Aporm
對獲得的表達式作以下變換,其中「|」符號表示按列展開
繼續對其中的加法表達式作變換,即擴展到齊次座標空間作平移變換
如今,咱們獲得了座標空間C到座標空間P的變換矩陣Mc->p
能夠看出Mc->p其實是經過座標空間C在座標空間P中的原點和座標軸的矢量表示構建出來的:把3個座標軸依次放入矩陣的前3列,把原點矢量放到最後一列,再用0和1填充最後一行便可。對象
咱們能夠利用反向思惟,從這個變換矩陣中提取出座標空間C的原點和座標軸在座標空間P的表示。例如,當咱們已知從模型空間到世界空間的4×4變換矩陣,咱們能夠提取出它的第一列,再進行歸一化(爲了消除縮放的影響)來獲得模型空間的x軸在世界空間下的單位矢量表示。一樣的方法能夠提取y軸和z軸。blog
當對方向矢量進行座標空間變換時,因爲矢量是沒有位置的,所以座標空間的原點變換是能夠忽略的。那麼對方向矢量的座標空間變換就可使用3×3的矩陣來表示,即
在Shader中,咱們經常看到截取變換矩陣的前3行前3列來對法線方向,光照方向進行空間變化,這正是緣由所在。遊戲
一旦求出來Mc->p,Mp->c就能夠經過求逆矩陣的方式求出來,由於從座標空間C變換到座標空間P與從座標空間P變換到座標空間C是互逆的兩個過程。當Mc->p是一個正交矩陣時,Mc->p的逆矩陣就等於它的轉置矩陣,即
此時,咱們還能夠經過Mc->p反推出座標空間P的座標軸在座標空間C中的表示Xp, Yp, Zp,這些座標軸對應的就是Mc->p的每一行。ip
模型空間,是和某個模型或者說是對象有關的,模型空間也被稱爲對象空間或局部空間。
每一個模型都有本身獨立的座標空間,當它移動或旋轉的時候,模型空間也會跟着移動和旋轉。
Unity在模型空間中使用的是左手座標系,所以在模型空間中,+x軸,+y軸,+z軸分別對應的是模型的右,上,前向。
模型空間的原點和座標軸一般是由美術人員在建模軟件裏肯定好的。當導入到Unity中後,咱們能夠在頂點着色器中訪問到模型的頂點信息,其中就包含了每一個頂點的座標。這些座標都是相對於模型空間中的原點(一般位於模型的重心)定義的。數學
世界空間是一個特殊的座標系,由於它創建了咱們所關心的最大空間,即整個遊戲空間
在Unity中,世界空間一樣使用了左手座標系。它的x軸,y軸,z軸是固定不變的。
頂點變換的第一步,就是將頂點座標從模型空間轉換到世界空間中,這個變換一般叫作模型變換
在Unity中,咱們能夠經過Transform組件中的值得知模型作了哪些變換。這個值是根據Transform的父節點的模型座標空間中的原點定義的,若是這個Transform沒有任何父節點,那麼這個值就是相對於世界座標空間定義的。it
要將模型空間中的一點轉換到其父空間中,須要獲取M子->父,這個矩陣能夠經過模型的Transform值獲得。Transform中包含了旋轉,縮放和平移值,則M子->父 = Mtranslation Mrotate Mscale。而從模型空間轉換到世界空間的變換矩陣M模型->世界能夠經過子空間到父空間變換矩陣,父空間到爺爺空間變換矩陣,連乘,直到世界空間爲止獲得。io
觀察空間也被稱爲攝像機空間,能夠認爲是模型空間的一個特例,即攝像機的模型空間。
在Unity中,觀察空間使用的是右手座標系,即+x軸指向右方,+y軸指向上方,+z軸指向攝像機後方
頂點變換的第二步就是將頂點座標從世界空間變換到觀察空間中。這個變換一般叫作觀察變換。
從觀察空間到世界空間的變換矩陣咱們一樣能夠經過Transform中的值獲得,再對該矩陣求逆獲得從世界空間到觀察空間的變換矩陣。咱們還可使用另外一種方法,對Transform組件中的值直接取反(作逆向變換),而後獲得從世界空間到觀察空間的變換矩陣。注意,因爲觀察空間使用的是右手座標系,所以還須要對變換矩陣的z份量進行取反操做。
頂點接下來要從觀察空間轉換到裁剪空間中,這個變換能夠被稱爲投影變換。這個用於變換的矩陣叫作裁剪矩陣或是投影矩陣
裁剪空間的目的是可以方便地對渲染圖元進行裁剪:徹底位於這塊空間內部的圖元將會被保留,徹底位於這塊空間外部的圖元將會被剔除,與這塊空間邊界相交的圖元就會被裁剪。而這塊空間就是由視椎體來決定的。
視椎體有兩種類型,分別對應兩種投影類型:透視投影(下圖左)和正交投影(下圖右)。透視投影模擬了人眼看世界的方式,而正交投影則徹底保留了物體的距離和角度。
投影矩陣雖然叫作投影矩陣,但並無真正進行投影,而是爲投影作準備。目的是對x,y,z份量進行縮放,通過投影矩陣的縮放後,咱們能夠直接使用w份量做爲範圍值,只有x,y,z份量都位於這個範圍內的頂點才認爲是在裁剪空間內。而且w份量在真正的投影時也會用到。
透視投影和正交投影分別對應了不一樣的投影矩陣。還須要注意的是投影矩陣會改變空間的旋向性:空間從右手座標系變換到了左手座標系
其中FOV表示視椎體垂直方向的張開角度,而Near和Far分別控制了近裁剪平面和遠裁剪平面距離攝像機的遠近。這樣咱們能夠求出近裁剪平面的高度,以下所示。遠裁剪平面相似。
而根據攝像機的橫縱比信息,我麼就能夠獲得近裁剪平面的寬度
一個頂點和透視投影的投影矩陣相乘後獲得的結果以下
視椎體的變化以下所示
此時咱們就能夠按以下不等式來判斷一個變換後的頂點是否位於視椎體內
其中Size表示視椎體豎直方向上高度的一半,而Near和Far一樣分別控制了近裁剪平面和遠裁剪距離攝像機的遠近。則近裁剪平面的高度以下所示。遠裁剪平面相似。
近裁剪平面的高度一樣能夠經過攝像機的縱橫比獲得
一個頂點和正交投影的投影投影矩陣相乘後獲得的結果以下
視椎體的變化以下所示
判斷一個變換後的頂點是否位於視椎體內使用的不等式和透視投影中的同樣,這種通用性也是爲何要使用投影矩陣的緣由之一。
當完成了全部的裁剪工做後,就須要進行真正的投影了,即把視椎體投影到屏幕空間中,這個過程能夠被稱爲屏幕映射。通過這一步變換,咱們會獲得真正的像素位置,對應的2D座標,而不是虛擬的三維座標。這個過程能夠理解成有兩步:
在Unity中,屏幕空間左下角的像素是(0, 0),右上角的像素座標是(pixelWidth, pixelHeight)。齊次除法和屏幕映射的過程可使用下面的公式來表示
$$screen_x = \frac{clip_x * pixelWidth}{2 * clip_w} + \frac{pixelWidth}{2}$$
$$screen_y = \frac{clip_y * pixelHeight}{2 * clip_w} + \frac{pixelHeight}{2}$$
在Unity中,從裁剪空間到屏幕空間的轉換是由底層幫咱們完成的。咱們的頂點着色器只須要把頂點轉換到裁剪空間便可(模型空間-世界空間-觀察空間-裁剪空間,對應的矩陣一般會串聯成一個MVP矩陣)。
最後,咱們再來看一種特殊的變換:法線變換。在遊戲中,模型的一個頂點每每會攜帶額外的信息,而頂點法線和切線就是其中的兩種信息,切線和法線是互相垂直的。
因爲切線是由兩個頂點之間的差值計算獲得的,所以咱們能夠直接使用變換頂點的矩陣MA->B來變換切線。但若是直接使用MA->B來變換法線,獲得的新法線可能就不會和切線垂直了。例以下圖所示.
那麼應該使用哪一個矩陣來變換法線呢?咱們能夠經過數學約束條件推出這個矩陣。因爲頂點法線NA和切線TA垂直,則TANA = 0。給定變換矩陣MA->B,咱們已知TB = MA->BTA。如今咱們要找到一個矩陣G來變換法線NA,使得變換後的法線仍然與切線垂直,即
經過一些推導可得
因爲TANA = 0,所以可得
即
這說明使用原變換矩陣的逆轉置矩陣來變換法線就能夠獲得正確的結果。
值得注意的是,若是矩陣MA->B是正交矩陣,則咱們能夠直接使用原變換矩陣做爲法線的變換矩陣。若是變換隻包含旋轉和統一縮放,咱們能夠利用統一縮放係數k來獲得變換矩陣MA->B的逆轉置矩陣,這樣能夠避免計算逆矩陣的過程。
視口空間中的座標被稱爲視口座標,就是把屏幕歸一化,這樣屏幕左下角就是(0, 0),右上角就是(1, 1)。若是已知屏幕座標的話,咱們只須要把x,y份量除以屏幕分辨率便可獲得視口座標。若是已知裁剪空間中的座標,能夠經過如下公式獲得視口座標