課程鏈接:GAMES101-現(xiàn)代計算機圖形學入門-閆令琪
課程講師:閆令琪
本系列筆記為本人根據(jù)學習該門課程的筆記,僅分享出來供大家交流,希望大家多多支持GAMES相關講座及課程,如涉及侵權請聯(lián)系我刪除:albertlidesign@gmail.com
經過上一節(jié)我們將所有的物體都映射到了一個的立方體里,那么下一步我們該怎么辦?下一步就是將這些物體畫在屏幕上,這一步就叫做光柵化 (Rasterization)。
Canonical Cube to Screen
屏幕的定義
既然要畫在屏幕上就需要把屏幕的概念定義好:
- 它是一個包含像素 (pixels) 的數(shù)組 (array)
- 數(shù)組的尺寸:分辨率 (resolution),例如
- 屏幕是一個典型的光柵成像設備
Raster在德語里就是screen,光柵化Rasterize == drawing onto the screen
像素pixel是"picture element"的簡寫,在這里我們定義一個像素是一個顏色均勻的小正方形,它包含三個值。
屏幕空間
屏幕空間就是在屏幕上建立一個坐標系,約定俗成地,以左下角為原點,向右為
,向上為
,因此任何屏幕上的點都可以用
來表示。
- 每一個像素的坐標都是用
來表達,這里的
和
均為整數(shù)。例如圖中藍色的像素為
- 如果我們定義屏幕的分辨率為
,則所有的像素的坐標的范圍為從
到
- 像素
的中心點位于
。例如藍色像素的中心為
- 整個屏幕覆蓋的范圍為從
到
因此為了完成的立方體映射到屏幕這一操作,我們需要將物體的
坐標移去,將
平面上的
變換到
。我們只需將寬度和高度都除以
,然后再將它的中心從
移動到屏幕空間左下角,也就是移動寬度除以
和高度除以
的距離。視口變換的表達式如下:
到這一步時,我們已經得到平面上的圖,三維空間中的網(wǎng)格模型經過以上變換,變成了屏幕空間中的多邊形,我們需要把這些多邊形進一步“打碎”,打成像素,變成每一個像素上的顏色值,這就是我們所說的光柵化。
Triangle Meshes
為什么選用三角形網(wǎng)格?
- 它是最基本的多邊形
- 任何多邊形都可以被拆分成多個三角形
三角形網(wǎng)格的獨特性質
- 三角形一定是平面圖形。三點共面
- 三角形的內外定義清晰。可以通過向量叉積來定義一個點在三角形內還是三角形外
- 面內插值方便。只要定義三角形三個頂點不同的屬性,就可在三角形內部做這個屬性的漸變效果,也就是說,通過三角形面內的一個點和其他點的位置關系,可以得到一個插值。(重心坐標插值方法)
通過屏幕上三個頂點坐標,可以知道一個三角形在屏幕中的位置,如左圖所示。為了繪制出這個三角形,我們需要判斷每個像素的中心點是否在三角形內部,這里介紹一個最簡單的方法:采樣。
采樣
采樣就是給定一個連續(xù)的函數(shù),在不同的點處求它的函數(shù)值。也就是說,采樣就是把一個連續(xù)的函數(shù)離散化的過程。
for(int x=0; x< xmax; ++x)
output[x] = f(x);
采樣是一個非常重要的概念,在圖形學里會涉及到各種各樣的采樣,我們可以采樣時間、面積、方向、體積等等。我們這里說的采樣,是指利用像素中心對屏幕空間進行采樣,也就是說我們需要求出某一個函數(shù)在屏幕中不同的像素中心的值。
我們這里的采樣就是去判斷每一個像素的中心是否在三角形的內部,因此我們可以定義出這個函數(shù):
Rasterization = Sampling A 2D Indicator Function
我們使用兩個for循環(huán)來遍歷所有的像素,對每一個像素執(zhí)行采樣方法即可完成光柵化的過程:
for (int x = 0; x < xmax; ++x)
for (int y = 0; y < ymax; ++y)
image[x][y] = inside(tri, x + 0.5, y + 0.5);
那么如何定義函數(shù)呢?也就是說我們需要去判斷一個點是否在一個三角形內。我們可以用向量叉積的方法來判定,例如,如圖所示,我們需要判斷點
是否在三角形
內,我們需要執(zhí)行如下過程(注意頂點順序):
- 計算
,如果得到的向量的
值為正,則
在向量
的左側,否則在右側
- 計算
,如果得到的向量的
值為正,則
在向量
的左側,否則在右側
- 計算
,如果得到的向量的
值為正,則
在向量
的左側,否則在右側
當三個叉積向量全部為正或負(同號)時,該點在三角形內部。
這里可能會有一種情況,剛好點在三角形邊界上時該如何判斷?要么不做處理,要么特殊處理。即點到底在三角形還是三角形
上,可以自己定義標準,可以定義點既在
又在
上,也可以定義點不在這兩個三角形上。
上面我們提到了通過采樣來進行光柵化的過程,那么我們想,我們已經寫了一個二重循環(huán),也就是說考慮一個三角形的光柵化就需要將所有的像素都跑一遍,其實是沒必要。一個三角形其實只能覆蓋一個相對較小的區(qū)域,比如圖中左邊第一列根本不在藍色的區(qū)域,就不可能碰到三角形,也就根本不用去考慮這些像素。藍色的區(qū)域我們稱為三角形的包圍盒(Bounding Box),更嚴格地說是一個軸向包圍盒(Axis Aligned Bounding Box,AABB)。求三角形的包圍盒非常簡單,只需要使用三個點最小和最大的值和
值來構造一個區(qū)域。這樣只有當這個三角形很窄長并且旋轉45度左右的時候,才會用一個很大的Bounding Box來包住
還有一種方法是將三角形覆蓋的區(qū)域中,每一行都去找它的最左和最右,這樣的話一個多余的像素都不會多考慮。
經過以上操作,我們得到了如圖所示結果,我們會發(fā)現(xiàn)這個結果不太對,因為它有鋸齒(Jaggies),這是因為我們的采樣率對信號來說是不夠高的,所以產生了走樣(Aliasing)。因此我們要使用抗鋸齒(也稱反走樣)技術來進一步優(yōu)化。
經過以上操作,我們得到了如圖所示結果,我們會發(fā)現(xiàn)這個結果不太對,因為它有鋸齒(Jaggies),這是因為我們的采樣率對信號來說是不夠高的,所以產生了走樣(Aliasing)。因此我們要使用抗鋸齒(也稱反走樣)技術來進一步優(yōu)化。
反走樣 (Antialiasing)
我們用每一個像素的中心去檢測是否在三角形內,然后把對應的像素涂上顏色,我們最后繪制出來的圖案就是帶有鋸齒的圖像。事實上我們不希望有鋸齒,因此我們需要抗鋸齒和反走樣。鋸齒的學名叫走樣(Aliasing),反走樣就稱為Antialiasing。
采樣理論
前面有提到,采樣是在圖形學中廣泛存在的一個做法。光柵化的過程其實就是在屏幕空間離散的點上進行是否在三角形內的采樣。對于任何一個我們拍出來的照片,放大后就會發(fā)現(xiàn)很多格子,也就是像素,這也是我們表示圖像的基本方法。一副照片其實就是所有到達感光元件的光學信息,通過把它離散成圖像像素的過程,其實也是采樣。采樣不光可以發(fā)生在不同的位置,也可以發(fā)生在不同的時間,視頻就是在時間中進行采樣。因此采樣是廣泛存在的,同樣,采樣所產生的問題也是廣泛存在的。
Sampling Artifacts in Computer Graphics
采樣所產生的錯誤(或稱瑕疵)稱為Artifacts。采樣產生的第一個問題就是鋸齒問題,如圖所示。
然后是摩爾紋問題。如果我們把下左圖中的奇數(shù)行和奇數(shù)列的像素都去掉,然后再重新對在一起,再顯示原圖大小,就會出現(xiàn)摩爾紋效果。當我們拿手機去拍顯示器的屏幕也會看到類似的效果,這些都是采樣帶來的問題。
接著是車輪效應,當我們順時針旋轉紙片,如圖所示,會有些條紋顯得像是在逆時針旋轉。在生活中也經常看到類似的現(xiàn)象,比如高速行駛的汽車,其輪子看上去在反向旋轉,這是因為人眼在時間中的采樣跟不上運動速度所造成的。
因為采樣所產生的問題有:
- 鋸齒問題 (Jaggies) - sampling in space
- 摩爾紋問題 (Moire) - undersampling images
- 車輪效應 (Wagon wheel effect) - sampling in time
- Many more...
采樣產生問題的本質其實是信號變化過快導致采樣速度跟不上。因此我們要通過頻率來分析它。
Antialiasing Idea: Blurring (Pre-Filtering) Before Sampling
如何進行反走樣,答案是在采樣之前先做模糊(或濾波)。之前我們采樣會發(fā)現(xiàn),有些點完全在三角形內,有些點完全在三角形外,因此這些點要么是紅的要么是白的。
但是如果我們在拿到一個三角形后,先做一個模糊操作,把它變成一個模糊的三角形,然后再去用像素中心點采樣,這樣就可以解決鋸齒問題,得到下圖效果。
舉個例子,上圖中,左圖為抗鋸齒操作之前,右圖為抗鋸齒操作之后。但是,先采樣再模糊是不行的。下圖是效果對比:左圖是先采樣再模糊,右圖是先模糊再走樣。先采樣再模糊的操作稱為Blurred Aliasing,先模糊再采樣的操作稱為反走樣 Antialiasing
那么,為什么采樣速度跟不上信號變化的速度就會產生走樣?為什么要先做采樣再做模糊達不到發(fā)走樣的效果呢?為了弄清楚這些事我們需要了解頻域(Frequency Domain)方面的知識
Frequency Domain
我們來看最簡單的波:sines and cosines。通過調整前面的系數(shù),例如
和
,我們能得到不同的余弦波,它們的不同在于頻率不同,我們定義
,其中
為頻率,因此
的頻率
,因此我們可以用頻率
來定義余弦波的變化有多快。同樣道理,我們還可以定義它的周期
,通俗地講,所謂周期就是每隔多少
,函數(shù)會重復自己一次,例如
每隔
會重復一次,
每隔
會重復一次。因此周期是頻率的倒數(shù)。
那么為什么我們要介紹周期和頻率呢?因為傅里葉變換(Fourier Transform)會用到它們。微積分里面有一個概念是函數(shù)展開,其中一個就是傅里葉級數(shù)展開。
傅里葉級數(shù)展開就是說,對于任何一個周期函數(shù),都可以把它寫成一系列正弦和余弦函數(shù)的線性組合以及一個常數(shù)項。
這樣我們就可以把一個函數(shù)描述成很多不同的正弦余弦項的和。這說明傅里葉級數(shù)展開和傅里葉變換是緊密相連的。給定任何一個函數(shù),我都可以經過一個復雜的操作變成另外一個函數(shù),也可以把變換后的函數(shù)通過逆變換變回原來的函數(shù),這樣的操作就稱為傅里葉變換和逆傅里葉變換。
不同的函數(shù)都有著不同的頻率,仔細上圖傅里葉級數(shù)展開的中的系數(shù)分別為
這些值都代表了不同的頻率,也就是說通過傅里葉級數(shù)展開可以將任何一個周期性函數(shù)分解為不同的頻率。所謂傅里葉變換其實就是把函數(shù)變成不同的頻率的段,并且把這些不同頻率的段顯示出來。
舉個例子,如上圖所示,通過傅里葉變換我們得到了這個不同的函數(shù),我們知道這些函數(shù)都含有不同的頻率,假設我們對這些函數(shù)進行一次相同間隔的采樣我們會發(fā)現(xiàn)如下圖所示結果
從到
會發(fā)現(xiàn),
的采樣效果非常差。這也就是說通過頻率分析,我們可以體會到,對于一個函數(shù)來說,它本身有一定的頻率,采樣也有一定的頻率,但是如果我們采樣的頻率很低而函數(shù)本身的頻率很高,就會跟不上它的變化,就沒有辦法將原始的信號擬合出來。
再看一個例子,藍色線是函數(shù)本身,黑色線為采樣得到的函數(shù)。現(xiàn)在如果我們把黑色線看作是另一函數(shù),我們發(fā)現(xiàn),如果我們用同樣的一個采樣方法采樣兩種截然不同的信號,我們會得到完全相同的結果。即采樣頻率相差很多的藍色的函數(shù)和黑色的函數(shù)所得到的結果完全相同。這一現(xiàn)象就稱為“走樣”。
Filtering
現(xiàn)在我們知道了走樣的定義,也知道了傅里葉變換,那么就可以開始真正的分析一些函數(shù)倒底擁有怎樣的頻率。這里有一個重要的概念——濾波(Filtering),它就是把某個特定的頻段去除。傅里葉變換可以幫助我們理解這個問題,它可以將函數(shù)從實域變到頻域。如下圖所示,左圖是一個人的圖像,也就是實域,我們將其作傅里葉變換,得到右圖為頻域。
那么右邊這幅圖應該如何理解呢?中心是左邊這張圖最低頻的區(qū)域,四周為高頻區(qū)域,也就是說從中心到四周,其頻率會越來越高。其亮度表達了左圖在該頻域的信息量。這張圖就說明左圖大多數(shù)的信息都是集中在低頻上的,高頻的信息相對于低頻會少很多,實際上自然中所有的圖片基本都是這樣的。有人可能會問為什么圖片中有一個十字,水平一條線、豎直一條線很明顯。簡單地說,這是因為在分析一個信號時它是一個周期重復的信號,對于沒有周期重復的信號的話就將無限多個圖像疊放,因為很少有圖像左邊界和右邊界完全一致,不一致的情況下,我們把這個圖像的左側放到它的右側,就會產生一個極高的高頻,傅里葉變換就會產生這兩條線,為了分析,我們暫時忽略這兩條線。也就是說,傅里葉變換能夠讓我們看到這個圖像在各個不同的頻率的樣子,我們稱之為頻譜。
接著,如果我們使用濾波,去掉頻域中心的內容,也就是去掉低頻的內容,保留高頻的區(qū)域,然后我們再使用逆傅里葉變換得到實域,就會得到如圖所示結果。從圖中我們可以看到,高頻信息表示的其實就是圖像內容上的邊界。這樣的濾波稱為高通濾波(High-pass filter)。那么通常我們所說的邊界其實就是指圖像上的某一區(qū)域的周圍發(fā)生了劇烈的變換,也就是所謂的邊界,比如人的衣服和背景之間的色差,這樣的信息就是高頻信息,那么如果我們只顯示高頻的信息,得到的就是圖片中各物體的邊界。
再反過來,將高頻信息全部去除,只保留低頻信息,得到如下圖所示結果。我們會發(fā)現(xiàn)得到了一張相對模糊的圖,絕大部分細節(jié)被去除了。這里就是應用了低通濾波器(Low-pass filter)。邊界被去除了就會變得模糊。
接下來,我們將高頻和最低頻信息都去除,保留中間頻率的信息,得到下圖所示結果。我們得到了不是很明顯的邊界特征,因為最明顯的邊界特征對應最高的頻率。
更多關于這方面的信息可以去了解一門課,叫《數(shù)字圖像處理》,圖形學中就不再過多贅述了。這是一個經典操作,現(xiàn)在更多的操作是通過機器學習來完成的。
Convolution Theorem
我們說濾波其實就是去掉特定頻率的信息,從另外一個角度上看,濾波又等于卷積,或者平均,F(xiàn)iltering = Convolution ( = Averaging)。平均的概念比較好理解,低通濾波器就是將圖片模糊,也就是一種平均操作。然后卷積是什么呢?
如上圖所示,對于一系列信號數(shù)組,濾波器就好像一個窗口,它可以左右移動,窗口的大小是3個格子,這其實是要做一個卷積(Convolution)操作。這是在說,在移動這個窗口的時候,窗口三個數(shù)所對應的信號的三個數(shù)做一個點積操作,如下圖所示,最后將結果寫回這個格子。對所有格子做這樣的操作,就會得到新的數(shù)組。注意這里的卷積定義不是數(shù)學上的定義,是圖形學簡化的定義。
卷積理論:實域上如果要對兩個信號進行卷積,其實,對應到兩個信號各自的頻域上,就是兩個信號的頻域的乘積。在實域上如果是對兩個信號進行卷積,那就是頻域上對其信號的乘積。通過這個理論我們可以直接對圖進行一個卷積操作,也可以將這幅圖先用傅里葉變換,變換到頻域上,再把卷積的濾波器變到頻域上,兩者相乘,得到頻域上的結果,再把它逆傅里葉變換變回實域,這兩個操作是一樣的。
我們對圖片進行一個平均或者是卷積操作,即對任何一個像素,取它周圍個像素的平均,那么得到的結果就是一個模糊的圖像。我也可以使用傅里葉變換的方法來做這樣的操作,最后通過逆傅里葉變換變回這幅圖。實域卷積=頻域乘積,頻域卷積=實域乘積。
再舉一個例子,下左圖為實域,右圖為頻域,如果左圖中的方形變大了,頻域會如何變換呢?
答案如下圖所示,變小了。因為我們?yōu)榱四:粡垐D,用了一個的卷積操作,如果我們換成一個
的方形做卷積,那么得到的結果會越來越模糊,也就更接近于低頻。如果我們用一個很小的方形做卷積,那么對應頻域的范圍就會很大,也就能留下更高的頻域,最后模糊的效果就會不明顯。
從頻率的角度看采樣
下面我們再從頻率的角度來看什么是采樣。采樣就是在重復頻率或者說頻域上的內容Sampling = Repeating Frequency Contents。
如上圖所示,圖為某個連續(xù)的函數(shù),其頻域為圖
,假設我們要采樣這個函數(shù),就要取一系列離散的點,留下這些離散點的函數(shù)值。也就是讓函數(shù)
去乘另外一個函數(shù),這個函數(shù)只在一些固定的位置有值,其他地方沒有值,也就是圖
,這樣的函數(shù)稱為沖擊函數(shù)。我們令
函數(shù)乘
函數(shù),得到的就是圖
函數(shù),這就是采樣。也就是給定一個原始函數(shù)
,我們乘上一個沖擊函數(shù)就可以得到采樣結果。在頻域上,沖擊函數(shù)的頻域還是沖擊函數(shù),只不過間隔有所變化。我們記得實域的乘積對應到頻域上是卷積,即
卷積
,最后的結果就是將
重復了很多次。那也就是說,采樣就是在重復原始信號的頻譜。
所以,如果我們采樣地不夠快,原始信號重復的間隔就會非常小,意味著采樣之間的距離很大,采樣的越稀疏,頻譜就越密集,頻譜越密集,采樣就越稀疏。如果頻域上重復的時候疊在一起了,就發(fā)生了走樣現(xiàn)象。所謂走樣在頻率圖像上發(fā)生了混疊。
圖形學中的反走樣
增加采樣率是終極解決辦法,直接更換硬件來提高分辨率,例如用視網(wǎng)膜顯示器來看圖形,高分辨率意味著采樣率高,意味著頻譜與頻譜之間的搬移間隔大,就不容易出現(xiàn)頻譜的混疊。但是這并不是反走樣要做的事情,反走樣不會增加分辨率。
反走樣操作:先模糊再采樣。通過前面的講解,我們知道先模糊再采樣這一操作是有意義的。模糊就是用低通濾波將高頻信息拿掉,然后再采樣。
為什么這樣是正確的?我們再看一個例子,一個函數(shù)的頻譜就是如圖所示的梯形,稀疏的采樣就會發(fā)生頻譜的混疊,產生走樣。如果我們先做一個模糊,也就是把高頻信號砍掉,也就是去掉虛線方塊之外的信息,剩下的就是下圖的信號。然后再采樣,就不會發(fā)生混疊了。
在實際的操作中,我們用什么樣的方法來對圖像進行模糊呢?用一個一定大小的低通濾波器來進行卷積。最簡單的塊就是一個像素,一個像素就是一個box filter。因此我們的解決方案為:
- 原本的函數(shù)
,判定了一個三角形要么在三角形里要么在三角形外,這就是一個二值函數(shù),我們用每一個像素對它進行一個卷積操作,也就是求一個平均,然后用平均值的中心采樣(其實不需要采樣,因為就一個像素對應一個box filter,就只有一個值)
對于任何一個像素,我們都能知道它可能被覆蓋,我們對它的覆蓋面積求平均,然后根據(jù)覆蓋得多少賦值。
Antialiasing By Supersampling (MSAA)
那么我們如何把一個像素被三角形覆蓋的區(qū)域算出來呢?實現(xiàn)并不容易,因此人們研究出一種近似算法,稱為Antialiasing By Supersampling (MSAA),它是一個反走樣的近似,并不能嚴格意義地解決反走樣的問題。
算法是將每一個像素劃分成許多小像素,每一個小像素有一個中心,我們可以判斷這些點在三角形內,再將這些點平均起來,如果點足夠多就能得到比較好的結果。其實就是在一個像素內部增加采樣點。例如一個像素里,如果個采樣點都不在三角形內,就判定它不在三角形內,如果有一個采樣點在三角形內,覆蓋率就為
,如果三個點在,覆蓋率就為
。通過這樣的方法就能實現(xiàn)抗鋸齒的效果。
因此MSAA解決的是對信號的模糊的操作,而下一步采樣的操作被隱含在這一步里了。MSAA不是靠提升分辨率和采樣率來直接解決走樣問題。
通過MSAA,我們得到了一個不錯的反走樣效果,但是為了引入MSAA,我們犧牲了什么?很顯然,我們使用了更多的點來測試是否在三角形內,也就是增大了計算量。因為這些點只是為了檢測三角形的覆蓋而已,卻要付出很多倍的計算量。為了減少計算量,在工業(yè)界上不會規(guī)則地劃分成均勻的個點,而會用一些分布不均的點來使很多樣本得到復用。
FXAA和TAA
其他的具有里程碑意義的方法有:FXAA (Fast Approximate AA) 和TAA (Temporal AA)。FXAA是圖像的后期處理方法,先把有鋸齒的圖得到,再通過一種操作把鋸齒消掉。但是如果先得到鋸齒的圖,再做模糊操作是不對的,F(xiàn)XAA是通過圖像匹配的方法找到邊界然后替換成沒有鋸齒的邊界,效率非常高。TAA方法是最近幾年興起的方法,它可以尋找上一幀的信息,用像素內的一個點沒有做MSAA來感知,假如我們看到的是靜止的場景,相鄰兩幀看到的信息一樣,那么我們可以用不同位置上的點來感知覆蓋信息,那么大家會發(fā)現(xiàn)在時間范圍內,得到的邊界會各不相同。TAA是復用上一幀得到的像素的值,相當于將MSAA的樣本分布在時間上,這樣就沒有引入任何額外的操作,這是一個非常聰明的作法,對于運動物體,我們會在后面的實時光線追蹤繼續(xù)講解。
超分辨率問題
如果我們有一張的圖,我們想將其放大至
,但是我們又不想看到鋸齒,就也要解決樣本不足的問題,因此超分辨率問題和反走樣問題是非常相似的。有效的做法是DLSS(Deep Learning Super Sampling),利用深度學習來解決。