101:02-04
圖形學(xué)所需要的一些基礎(chǔ)學(xué)科(然而之前我不懂它們的重要都沒(méi)好好學(xué),淚目):
高數(shù),線代,概率論基礎(chǔ)物理(光學(xué)、力學(xué))信號(hào)處理?。。?!
罷了,好在也不是完全沒(méi)學(xué),起碼打下了基礎(chǔ)。

其實(shí)這部分內(nèi)容,之前數(shù)字媒體數(shù)學(xué)基礎(chǔ)的時(shí)候已經(jīng)學(xué)了挺多。上學(xué)期圖形學(xué)又復(fù)習(xí)了一遍,所以關(guān)于如何利用矩陣對(duì)向量進(jìn)行平移旋轉(zhuǎn)和縮放。我就不贅述了。
記錄一下之前沒(méi)太特意記的吧。和之前自己一些潛意識(shí)應(yīng)用的總結(jié)。
向量點(diǎn)乘:其實(shí)從數(shù)學(xué)上還有個(gè)快速算法,就是b在a上的投影的長(zhǎng)度乘以a的長(zhǎng)度。我對(duì)于點(diǎn)乘的概念,經(jīng)常解釋為判斷兩個(gè)向量的重合程度。圖形學(xué)官方則是用夾角來(lái)說(shuō)明,不過(guò)夾角也確實(shí)更準(zhǔn)確。首先b垂直a的時(shí)候,點(diǎn)乘是0,然后偏向于a那邊點(diǎn)乘就是正,遠(yuǎn)離就是負(fù)。按我的話來(lái)說(shuō),如果兩個(gè)都是單位向量,它們?cè)街睾?,點(diǎn)乘就越接近1,越相反,點(diǎn)乘越接近-1。

點(diǎn)乘的坐標(biāo)運(yùn)算,這個(gè)真的快忘記了,這里就復(fù)習(xí)一下吧。其實(shí)也挺好記。

點(diǎn)乘在圖形學(xué)里最大作用就是找?jiàn)A角。還有計(jì)算投影長(zhǎng),來(lái)把向量正交分解。比如我們已經(jīng)學(xué)的一些光照模型。
叉乘:兩個(gè)向量叉乘的結(jié)果是垂直于這兩個(gè)向量的新向量。并且遵循右手定則來(lái)決定方向。(如右圖,拇指伸直右手四指從a方向轉(zhuǎn)向b方向,拇指方向?yàn)榻Y(jié)果方向)長(zhǎng)度計(jì)算則如圖。

這個(gè)可以用來(lái)定義坐標(biāo)系。(比如之前的切線空間就是確定了法線和切線方向后,叉乘得出副切線方向)然后對(duì)于一個(gè)坐標(biāo)系的三個(gè)軸,如果xy叉乘得到z,那么它就是右手坐標(biāo)系,得-z則為左手。向量叉乘它自身結(jié)果為0。關(guān)于叉乘的快速用法,我這里介紹一下多年前高中時(shí)候從坤哥那里看來(lái)的方法??谠E:主減副,副減主,主減副。用法如右圖。

叉乘主要用途,用來(lái)判斷兩個(gè)向量的左右或者前后關(guān)系。 如右圖左,在一個(gè)右手坐標(biāo)系中,怎么判斷a和b的左右關(guān)系呢。就是看它們的叉乘結(jié)果是正還是負(fù)。其中,左指的是逆時(shí)針?lè)较?,右指的是順時(shí)針?lè)较?。也就是說(shuō)y正半軸在x正半軸的左邊。然后以圖中ab為例,a叉乘b,結(jié)果指向屏幕外,說(shuō)明b在a的左邊。(也許你會(huì)說(shuō)這不是一眼看出來(lái)嗎,但是你看到的只是一個(gè)平面上的映射,它們?cè)诳臻g中的關(guān)系你是不清楚的。)(好吧好像不是我前面解釋的角度,而是說(shuō),就算你一眼能看出來(lái),但是計(jì)算機(jī)沒(méi)眼睛啊,它看不出來(lái),只能通過(guò)數(shù)學(xué)方式推,圖右同理)(為什么結(jié)果指向屏幕外就說(shuō)b在a的左邊呢,你自己拿右手比劃一下,是不是逆時(shí)針轉(zhuǎn)就是指向外面的)?
而對(duì)于圖下,如何判斷p點(diǎn)是否在三角形ABC內(nèi)呢?按頂點(diǎn)順序,比如ABC,首先AB叉乘AP ,可以判斷P是在AB左邊,然后BC叉乘BP……以此類推,發(fā)現(xiàn)P點(diǎn)都在三個(gè)邊的左邊,那么它就在三角形內(nèi)部。(但是如果你不按abc順序來(lái)也沒(méi)關(guān)系,只要判斷這個(gè)點(diǎn)都在三條邊的同一邊,那么它就在內(nèi)部)?
這個(gè)功能通常在光柵化里大量應(yīng)用,用來(lái)判斷像素是否在三角形內(nèi)部。如果點(diǎn)在邊上,那么俗語(yǔ)稱(corner case),你自己做決定。

矩陣乘法:這里講了一個(gè)以前從沒(méi)想過(guò)的計(jì)算角度。我直呼喵啊。其實(shí)就是你知道結(jié)果是M行P列的那么你算結(jié)果的第x行y列的值的時(shí)候,就去找原來(lái)第一個(gè)矩陣的x行和第二個(gè)矩陣的y列來(lái)乘積求和。關(guān)于矩陣變換,我之前數(shù)字媒體數(shù)學(xué)基礎(chǔ)課研究了一些結(jié)論。我這里截圖一部分吧。后面想再了解可以去翻我的作業(yè)。


唉,現(xiàn)在學(xué)的都是大二上的數(shù)字媒體數(shù)學(xué)基礎(chǔ)的東西,然而我現(xiàn)在好多都記不清了,感覺(jué)像重新學(xué)一樣。不過(guò)算了,學(xué)過(guò)總是有意義的。正好這次撿起來(lái),做一個(gè)總結(jié),以后再忘了來(lái)這里復(fù)習(xí)。
縮放,旋轉(zhuǎn)變換都可以用基變換來(lái)解釋,向量乘以縮放,旋轉(zhuǎn)后的基(矩陣形式),就變成了變換后的向量。這種可以用一個(gè)矩陣表示的變換也叫線性變換。平移比較特殊,它無(wú)法用基的變換來(lái)表示,所以它不是線性變換。

為了用一個(gè)矩陣表示平移,我們引入齊次坐標(biāo)(homogeneous coordinates)。齊次坐標(biāo)中,點(diǎn)的w為1,向量的w為0。這樣設(shè)計(jì)有一個(gè)很妙的地方,保證了運(yùn)算的正確。即向量加向量等于向量,點(diǎn)減點(diǎn)等于向量等等。這里點(diǎn)加點(diǎn)的結(jié)果是兩個(gè)點(diǎn)的中點(diǎn)。為什么是這樣,這要說(shuō)到齊次坐標(biāo)的特性。齊次坐標(biāo)中同一個(gè)點(diǎn)的坐標(biāo)有多種寫(xiě)法,但只要w歸一后坐標(biāo)一樣,那它們就是同一個(gè)點(diǎn)。(我們可以想象在一個(gè)透視的空間中,雖然兩個(gè)點(diǎn)離你遠(yuǎn)近不同,但是只要它們?cè)谀阊劾镏睾狭?,那就是同一個(gè)點(diǎn))關(guān)于齊次坐標(biāo)還有很多比較奇特的定理,比如點(diǎn)線面關(guān)系等等,在數(shù)字媒體數(shù)學(xué)基礎(chǔ)的課件里面都有,我就不一一總結(jié)了,用到再去看吧。



總之,當(dāng)引入齊次坐標(biāo)后,前面的平移問(wèn)題就有了解決方法。而這種變換也叫做仿射變換(Affine Transformations)

我深入思考了半天,嘗試從基變換的角度來(lái)理解為什么這個(gè)仿射變換可以造成平移的結(jié)果,下面給出我的結(jié)論。首先想象一個(gè)坐標(biāo)系以及空間中的網(wǎng)格,而平移變換表示w基在w=1這個(gè)平面上移動(dòng)了。這導(dǎo)致整個(gè)坐標(biāo)系空間產(chǎn)生了形變。(我不會(huì)做動(dòng)畫(huà)所以難以表示我想象的畫(huà)面,我嘗試用右圖來(lái)解釋。)


也就是說(shuō)這種位移是通過(guò)整個(gè)空間的變化來(lái)形成的投影上的位移(實(shí)際上,這個(gè)點(diǎn)在這個(gè)坐標(biāo)系內(nèi)的坐標(biāo)沒(méi)有任何改變)(可能你會(huì)說(shuō)那坐標(biāo)都變了怎么說(shuō)沒(méi)有改變,因?yàn)槲覀冇玫淖鴺?biāo)是直角坐標(biāo)系的基表示,而不是這個(gè)坐標(biāo)系的基表示)感覺(jué)還是有些抽象,以后我能想到更具象的解釋方法再補(bǔ)充吧。反正目前的我是理解了。
矩陣變換的順序十分重要??梢钥从覉D。

三維空間的矩陣變換,其實(shí)就是二維的拓展,沒(méi)什么好說(shuō)的。
好吧關(guān)于旋轉(zhuǎn)還是可以說(shuō)一下的。首先是基本的旋轉(zhuǎn)矩陣。我們知道旋轉(zhuǎn)可以通過(guò)一個(gè)軸和一個(gè)角度來(lái)表示的。

所以有一個(gè)公式,羅德里格斯旋轉(zhuǎn)公式,來(lái)通過(guò)一個(gè)軸和角度來(lái)表示旋轉(zhuǎn)。(這個(gè)軸默認(rèn)是過(guò)原點(diǎn)的)其實(shí)之前關(guān)于unity的旋轉(zhuǎn),我還做了一些研究。比如萬(wàn)向鎖和四元數(shù)的。四元數(shù)的作用其實(shí)主要是用于插值,如果我們直接通過(guò)歐拉角插值,結(jié)果往往不是正確的。不過(guò)我也忘了當(dāng)時(shí)怎么研究的了。有需要去翻我那時(shí)的筆記吧。


下面講觀測(cè)變換(Viewing transformation),其下又可分為視圖變換和投影變換。?


首先說(shuō)一下MVP變換,指的是模型(Modle),視圖(View),投影(Projection)變換。其中,MV主要是把模型和攝像機(jī)的坐標(biāo)從世界空間,變換到觀察空間(VS,以相機(jī)為原點(diǎn))中。 相機(jī)信息如何表示:需要一個(gè)點(diǎn),表示位置,一個(gè)前向量和一個(gè)垂直向量,表示相機(jī)方向和頂部方向。

然后接下來(lái)就是求坐標(biāo)系的變換矩陣了。這里我們?cè)俅螐?fù)習(xí)一下:假如有兩個(gè)基,a基和b基(矩陣形式),有一個(gè)向量v,如果我想把b基下的向量v(即vb)轉(zhuǎn)換為a基下的向量v(即va)那么va等于(vb)乘(a基下的b基) 。想想之前的TBN矩陣,我們要從切線空間b轉(zhuǎn)換到世界空間a,那么就要把向量乘世界空間下的切線空姐基坐標(biāo)構(gòu)成的矩陣。而這很容易拿到,因?yàn)榍芯€法線這些一開(kāi)始就是世界空間下的坐標(biāo)了。?
在這里,我們首先把相機(jī)移動(dòng)到原點(diǎn)位置。這里我思考了半天,為什么這里要平移而TBN矩陣不用平移呢?我得出的結(jié)論是:首先,視圖變換涉及到了模型頂點(diǎn)坐標(biāo)的位置的變換,而且哪怕從基變換的角度,從舊基到新基也是一個(gè)平移旋轉(zhuǎn)的過(guò)程,所以視圖變換這邊是完全正確的。而TBN矩陣那邊,則是因?yàn)樵谡麄€(gè)過(guò)程中,只涉及了方向的變換,沒(méi)涉及位置的變換,(我們知道,在空間中,方向的表示是唯一的,哪怕可視化后看起來(lái)你是從空間某一點(diǎn)出發(fā)的方向,但是在坐標(biāo)里你都是從原點(diǎn)出發(fā)的。因此,TBN矩陣的基變換其實(shí)只涉及了旋轉(zhuǎn)變換)所以不用平移這個(gè)步驟。
平移之后是旋轉(zhuǎn)操作,回憶上面的基的變換,我要把所有坐標(biāo)從世界空間轉(zhuǎn)換到觀察空間(或者相機(jī)空間),那么需要乘以觀察空間下的世界空間的基的方向坐標(biāo)。這不是很好求(不是直接加負(fù)號(hào)這么簡(jiǎn)單)。但是如果是世界空間下的觀察空間的基的方向坐標(biāo),則是直接就可以得到的。而這兩個(gè)基的變換矩陣之間的關(guān)系其實(shí)就是逆矩陣的關(guān)系。所以從世界空間變換到觀察空間的變換矩陣我們也得到了。
下面是P投影變換,分為正交投影和透視投影。

如右圖是一個(gè)簡(jiǎn)單的正交投影的理解。首先把相機(jī)按y上-z前的方向擺好。然后把所有物體丟掉z軸即可。然后把x,y的范圍映射到-1到1方便運(yùn)算。但是丟掉z軸,怎么判斷誰(shuí)前誰(shuí)后呢?

所以我們用的是一種正規(guī)的正交投影方法。首先劃定空間中一個(gè)長(zhǎng)方體,通過(guò)平移和縮放操作變成一個(gè)以原點(diǎn)為中心,長(zhǎng)寬高為2的正方體。
我剛開(kāi)始也有這個(gè)疑惑?為什么要專門(mén)劃定一個(gè)區(qū)域,還要平移?難道不是直接往-z截取一個(gè)區(qū)域嗎。后來(lái)參考一些資料后得出結(jié)論,這個(gè)長(zhǎng)方體其實(shí)就是可視區(qū)域,之所以有平移縮放,就是為了方便相機(jī)修改可視區(qū)域??傊@個(gè)正交投影的操作就是,劃定一個(gè)視野范圍,然后標(biāo)準(zhǔn)化這個(gè)區(qū)域。

然后是透視投影。想要理解這個(gè)我得先解釋清楚MVP的整個(gè)過(guò)程。一開(kāi)始我以為它的輸入是一個(gè)三維空間,輸出是一個(gè)二維平面,即投影變換后的近平面。但是后來(lái)搜資料后才明白,它的輸出也是一個(gè)三維空間,叫做標(biāo)準(zhǔn)視體空間(更有名一點(diǎn)的說(shuō)法似乎是叫裁剪空間Clip Space)(就正交投影最終得出來(lái)的那個(gè)正方體)(其實(shí)在不同的圖形API里面,標(biāo)準(zhǔn)視體空間的坐標(biāo)范圍也不一樣)。裁剪空間的坐標(biāo)是一個(gè)四維齊次坐標(biāo)系,空間中的物體在此狀態(tài)下進(jìn)行裁剪剔除等操作后,再通過(guò)透視除法(perspective divide)降維成三維的笛卡爾坐標(biāo)系,而這個(gè)降維后的坐標(biāo)系就叫做標(biāo)準(zhǔn)設(shè)備坐標(biāo)(Normalized Device Coordinate),這個(gè)空間也叫NDC(規(guī)范化設(shè)備坐標(biāo))空間。之后對(duì)于NDC內(nèi)的幾何體,再做一個(gè)變換,得到它們?cè)谄聊豢臻g的坐標(biāo)。這時(shí)候才可以說(shuō)結(jié)果是一個(gè)二維平面了。但是要注意,這個(gè)屏幕映射只是單純的從三維到二維的變換,不涉及繪制,也就是說(shuō)沒(méi)有什么遮擋關(guān)系之類的。幾何體的深度信息會(huì)被保存在深度緩存中,在后面的光柵化過(guò)程中再用來(lái)決定遮擋關(guān)系。
好,介紹完整個(gè)流程后,我再來(lái)說(shuō)這個(gè)透視投影。投影變換的本質(zhì)是把物體從觀察空間變換到裁剪空間。正交投影可以很方便的拉伸視體(那個(gè)長(zhǎng)方體)為一個(gè)裁剪空間(最終的正方體)。而透視投影則要從一個(gè)視錐體(棱臺(tái))拉伸為一個(gè)正方體。因此我們考慮這個(gè)步驟,先把它拉伸為一個(gè)長(zhǎng)方體,再做正交投影變換。這個(gè)長(zhǎng)方體有兩個(gè)原則:近平面大小和坐標(biāo)不變,遠(yuǎn)平面的z值和中心點(diǎn)位置不變,四周的點(diǎn)壓縮到和近平面一樣大。


那么這樣子,我們就可以確定所有平面的壓縮后的xy的值了。如圖,假設(shè)這個(gè)平面到原點(diǎn)距離是n(特別注意是距離不是z值,而且是變換前的距離)然后遠(yuǎn)平面到原點(diǎn)的距離是z,那么這個(gè)平面的壓縮后的x'y',就是如圖所示。

而知道變換前后的坐標(biāo)后,我們就可以求出變換矩陣了。但是目前只能確定124行,第三行確定不了。因?yàn)槲覀冎爸皇谴_定了平面壓縮后的xy值,但平面的z值沒(méi)確定過(guò)。(注意我們之前只是設(shè)定遠(yuǎn)近平面的z值不變,而中間這些平面的z值是否有變動(dòng)我們是沒(méi)說(shuō)過(guò)的,而事實(shí)上,它們的z值確實(shí)有變動(dòng))

那么我們要如何求這個(gè)變換矩陣的第三行呢?很簡(jiǎn)單,帶入前面那兩個(gè)特例。對(duì)于近平面來(lái)說(shuō),映射后坐標(biāo)值毫無(wú)變化。求得如圖中式子(假設(shè)n為近平面到原點(diǎn)距離,near嘛)。

而對(duì)于遠(yuǎn)平面來(lái)說(shuō),則是中心點(diǎn)映射后坐標(biāo)不變,求得圖中式子。(f為遠(yuǎn)平面到原點(diǎn)距離,far)

n,f都是已知的,未知量就AB,兩個(gè)未知量,兩個(gè)式子,很容易解方程。至此,投影變換的矩陣就求出來(lái)了。

而最后值得我們思考的一個(gè)點(diǎn)是,在遠(yuǎn)近平面中間的平面,它們的z值是怎么變化的呢?我先直接根據(jù)經(jīng)驗(yàn)猜測(cè)一下。如圖,對(duì)于一張透視的圖,相同的間隔在圖里面是隨著距離的增大而變短的。這意味著什么?我們想象一下這個(gè)空間,應(yīng)該不難想出它會(huì)發(fā)生如右圖的變換:越靠近n越接近原來(lái)的距離,越靠近f間隔越密。


也就是說(shuō)整體上,中間平面的z值往遠(yuǎn)平面那邊偏了。
下面我嘗試數(shù)學(xué)證明。證明了半天感覺(jué)有點(diǎn)難,遂放棄。在gpt幫助下找到個(gè)優(yōu)秀答案,不過(guò)也證實(shí)了我的猜想是正確的。
