參考課程P4:
https://www.bilibili.com/video/BV1X7411F744?p=4
注:關(guān)于這部分知識(shí),可以參考馮樂(lè)樂(lè) Unity Shader入門精要的4.6節(jié),會(huì)有實(shí)例講述MVP的全過(guò)程,我也會(huì)在后續(xù)筆記中做記錄。
我們可以這樣來(lái)描述視圖變換的任務(wù):將虛擬世界中以(x,y,z)為坐標(biāo)的物體變換到 以一個(gè)個(gè)像素位置(x,y) 來(lái)表示的屏幕坐標(biāo)系之中(2維),這確實(shí)是一個(gè)較為復(fù)雜的過(guò)程,但是整個(gè)過(guò)程可以被細(xì)分為如下幾個(gè)步驟:
- 模型變換(modeling tranformation):這一步的目的是將虛擬世界中或者更具體點(diǎn),游戲場(chǎng)景中的物體調(diào)整至他們應(yīng)該在的位置
- 攝像機(jī)變換(view/camera tranformation):在游戲中我們真正在乎的是攝像機(jī)(或者說(shuō)眼睛)所看到的東西,也就是需要得到物體與攝像機(jī)的相對(duì)位置
- 投影變換(projection tranformation):根據(jù)攝像機(jī)變換得到了所有可視范圍內(nèi)的物體對(duì)于攝像機(jī)的相對(duì)位置坐標(biāo)(x,y,z)之后,便是根據(jù)是平行投影還是透視投影,將三維空間投影至標(biāo)準(zhǔn)二維平面([-1,1]^2)之上 (tips:這里的z并沒(méi)有丟掉,為了之后的遮擋關(guān)系檢測(cè))
- 視口變換(viewport transformation):將處于標(biāo)準(zhǔn)平面映射到屏幕分辨率范圍之內(nèi),即[-1,1]^2 ->[0,width]*[0,height], 其中width和height指屏幕分辨率大小

M在前面的坐標(biāo)變換中已經(jīng)說(shuō)了,現(xiàn)在重點(diǎn)看view/camera tranformation。
一、view/camera tranformation
1.相機(jī)的定義

- 相機(jī)的位置 e
- 相機(jī)看向的方向 g
- 相機(jī)向上的方向 t
2.相機(jī)位置

上面的截圖說(shuō)的是,考慮到運(yùn)動(dòng)的相對(duì)性,如果相機(jī)和物體一起移動(dòng),那么拍出來(lái)的照片是相同的。沿著這種思路,把相機(jī)放在世界坐標(biāo)的原點(diǎn),并讓坐標(biāo)軸與世界空間重合,然后再讓物體移動(dòng),就能達(dá)到同樣的效果。
這里也介紹一下正常的思路,根據(jù)基變換的思路。要得到世界坐標(biāo)的物體在相機(jī)空間的坐標(biāo),可以把世界坐標(biāo)的基轉(zhuǎn)換到相機(jī)空間。以Unity舉例,我們更容易獲得的是攝像機(jī)在世界空間中的坐標(biāo),所以需要對(duì)這個(gè)變換進(jìn)行求逆,才能得到我們的目標(biāo)矩陣。
這兩種思路,最終選擇的是把相機(jī)移到原點(diǎn)的思路。視頻中說(shuō)到這樣做的好處:會(huì)讓操作得到簡(jiǎn)化
3.移動(dòng)相機(jī)的具體步驟

因?yàn)橄鄼C(jī)到達(dá)目前的位置,是先進(jìn)行了縮放、旋轉(zhuǎn),再平移?,F(xiàn)在為了恢復(fù)到原點(diǎn),就是逆過(guò)來(lái)操作,需要先做逆平移,再做逆旋轉(zhuǎn)。如下圖:

相機(jī)旋轉(zhuǎn)到世界坐標(biāo)軸的矩陣是很難寫的,但是反過(guò)來(lái)世界坐標(biāo)軸旋轉(zhuǎn)到相機(jī)當(dāng)前的基坐標(biāo)卻很簡(jiǎn)單,即從x旋轉(zhuǎn)到g叉乘t(也就是e),y旋轉(zhuǎn)到t,z旋轉(zhuǎn)到-g非常簡(jiǎn)單,如圖:

然后再去求這個(gè)變換的逆,就是我們想要的目標(biāo)了。而旋轉(zhuǎn)矩陣是一個(gè)正交矩陣,直接轉(zhuǎn)置即可得到最終答案:

上面的公式只是講述了原理,具體例可以參考馮樂(lè)樂(lè) Unity Shader入門精要的4.6節(jié)。
二、projection
1.正交投影 orthographic

把左側(cè)的立方體,映射到中心的標(biāo)準(zhǔn)立方體。這樣做是為了什么呢?大佬沒(méi)有講,有彈幕說(shuō)是為了裁剪。
其中,l,r,b,t,f,n對(duì)應(yīng)的是left,right,bottom,top,far,near。因?yàn)槲覀儸F(xiàn)在右手坐標(biāo)系,相機(jī)是向-Z方向看,所以離我們遠(yuǎn)的物體,它的Z值是更小的。這樣就有點(diǎn)反直覺(jué),因?yàn)楦杏X(jué)上,值越大,應(yīng)該是離得越遠(yuǎn)。如果是左手坐標(biāo)系,就不會(huì)有這個(gè)問(wèn)題。
操作步驟很簡(jiǎn)單,先平移,再縮放:

平移的數(shù)值是在求中點(diǎn),正負(fù)號(hào)可以忽略,圖中這個(gè)位置才是負(fù)的。然后縮放是因?yàn)橐诺揭粋€(gè)-1到1,即長(zhǎng)度為2的正方體里,縮放倍數(shù)就是r-l/(2/r-l)=2
有人提問(wèn),這樣處理之后,正方體就被拉伸了呀,答案是所有物體放到-1,1的立方體后,后續(xù)還會(huì)做視口變換。
2.透視投影 prerspective

把那個(gè)遠(yuǎn)平面擠壓成和近平面一樣大,再做一個(gè)正交投影就達(dá)到目的了。擠的要求:
- 近平面不變
- 遠(yuǎn)平面在收縮時(shí),Z軸的值不能變,即f值不能變
- 遠(yuǎn)平面的中心點(diǎn)不能變
為了做這個(gè)擠壓操作,可以切開(kāi)來(lái)看:

這里利用相似三角形即可,然后齊次坐標(biāo),是可以同時(shí)擴(kuò)大z倍的,就得到如下結(jié)果:

這里z為什么是unknown呢,彈幕里有人表示,兩個(gè)平面的z確實(shí)沒(méi)變,但中間的那些點(diǎn)都是不一樣的。

現(xiàn)在已經(jīng)能得到這個(gè)矩陣的一部分?jǐn)?shù)值了。剩下的事情就是算出問(wèn)號(hào)部分的。現(xiàn)在已知:
近平面的Z是不變的,即x,y,n,1。這里利用齊次坐標(biāo)的性質(zhì)變成這樣:

現(xiàn)在思路一下,什么樣的矩陣左乘x,y,n,1。能在第三行出現(xiàn)一個(gè)n^2呢,那必然是一個(gè)前兩個(gè)格子都是0的矩陣,這樣才能把x,y都消掉:

這里可能不好理解,為什么要構(gòu)造一個(gè)n^2,其實(shí)是類似高中時(shí)那種特殊值求函數(shù)的思路,也可以叫特殊系數(shù)法。
現(xiàn)在還一個(gè)條件,即遠(yuǎn)平面的中心點(diǎn)0,0,f,1映射完之后還是0,0,f,1。這里利用齊次坐標(biāo)的性質(zhì)變成這樣:

所以根據(jù)遠(yuǎn)平面就可以得到Af+B=f^2
然后結(jié)合上面根據(jù)近平面得到的結(jié)論:

兩個(gè)式子聯(lián)合:

現(xiàn)在可以完成最后一步了:
