GPU Gems1 - 9 有效的阴影体渲染
這章全面講述了用于實(shí)時(shí)陰影渲染中常見(jiàn)兩種流派之一的陰影體(Shadow Volumes)技術(shù),又稱模板陰影(Stencil Shadows)技術(shù),重點(diǎn)是得到正確的角度的情形,減少幾何圖形和填充率的消耗。
簡(jiǎn)單談?wù)勱幱吧杉夹g(shù),目前普遍采用的一般有三種:Planar Shadow、Shadow Mapping和Shadow Volume,前者類似投影,計(jì)算最簡(jiǎn)單,缺點(diǎn)只能繪制拋射在平面上的陰影;Shadow mapping利用站在光源處所沿光源法線看去所生成的深度圖來(lái)檢測(cè)場(chǎng)景中的體象素是否處于陰影中,缺點(diǎn)是光源與物體位置相對(duì)固定、且在極端情況下計(jì)算精度差,不太適合精確到象素的動(dòng)態(tài)光陰場(chǎng)合;Shadow Volume是目前最適合精確表現(xiàn)動(dòng)態(tài)光陰場(chǎng)景的技術(shù),適用性最廣,其典型的適用范例便是Doom 3,不足在于陰影體積引入了額外的頂點(diǎn)和面,加大了存儲(chǔ)和處理強(qiáng)度,同時(shí)渲染出的陰影比較硬,如果要實(shí)現(xiàn)軟陰影,仍需其他技術(shù)配合。
說(shuō)實(shí)話,GEM中這篇文章寫(xiě)的挺難懂的,我還是以自己的角度,從網(wǎng)上等資料來(lái)理解下陰影體渲染。參考的一些文章鏈接如下:
https://blog.csdn.net/jxw167/article/details/65435329
https://blog.csdn.net/wang371372/article/details/41086435
陰影體渲染創(chuàng)建一個(gè)陰影體積,并僅在其外部的物體上處理照明,我們將使用模板緩沖區(qū)作為算法的關(guān)鍵組件, 因此名稱 - ?Stencil Shadow Volume。陰影體積算法背后的想法是將光線減弱時(shí)創(chuàng)建的對(duì)象輪廓擴(kuò)展到一個(gè)Volume中,然后使用一些簡(jiǎn)單的模版操作將該Volume映射到模板緩沖區(qū)中。 關(guān)鍵的想法是,當(dāng)一個(gè)對(duì)象在Volume內(nèi)(因此在陰影中)時(shí),Volume前面的多邊形會(huì)對(duì)對(duì)象的多邊形進(jìn)行深度測(cè)試,并且該Volume后部的多邊形將失效相同的測(cè)試,或者說(shuō)不參與測(cè)試。
我們將根據(jù)稱為Depth Fail的方法設(shè)置模板操作,人們經(jīng)常使用更直接的方法稱為Depth Pass來(lái)實(shí)現(xiàn)陰影體積技術(shù),但是其致命缺點(diǎn)是當(dāng)視點(diǎn)在陰影中時(shí),會(huì)導(dǎo)致模板計(jì)數(shù)錯(cuò)誤,而Depth Fail的方法修復(fù)了該錯(cuò)誤。
我們?cè)谧笙陆怯幸粋€(gè)燈泡,一個(gè)綠色的物體(稱為遮擋物體),由于光而投下陰影, 在這個(gè)場(chǎng)景中也渲染了三個(gè)圓形的對(duì)象。 對(duì)象B被遮蔽,而A&C不是。 紅色箭頭限定陰影體積的區(qū)域(線的虛線部分不是它的一部分)。
我們首先將實(shí)際對(duì)象(A,B,C和綠色框)渲染到深度緩沖區(qū)中(從相機(jī)位置觀察的)。 當(dāng)我們完成后,我們可以獲得最接近的像素的深度。 然后我們一個(gè)接一個(gè)地遍歷場(chǎng)景中的對(duì)象,并為每個(gè)對(duì)象創(chuàng)建一個(gè)陰影體積。 這里的示例僅顯示綠色框的陰影體積,但在完整的應(yīng)用程序中,我們還將為圓形對(duì)象創(chuàng)建Volume,因?yàn)樗鼈兺渡渥约旱年幱啊?陰影體積是通過(guò)檢測(cè)它的輪廓來(lái)創(chuàng)建的并將其擴(kuò)展到無(wú)限遠(yuǎn)。 我們使用以下簡(jiǎn)單規(guī)則將該Volume渲染到模板緩沖區(qū)中:
1、如果在渲染陰影體積的背面多邊形時(shí)深度測(cè)試失敗,我們會(huì)增加模板緩沖區(qū)中的值。
2、如果在渲染陰影體積的前面多邊形時(shí)深度測(cè)試失敗,我們會(huì)減小模板緩沖區(qū)中的值。
3、在深度測(cè)試通過(guò),模板測(cè)試失敗情況下,我們什么都不做。
讓我們看看使用上述方案的模板緩沖區(qū)會(huì)發(fā)生什么。
物體A:渲染陰影體的背面時(shí)深度測(cè)試失敗(由于A的阻擋),所以模板緩沖值加1.渲染陰影體的前面時(shí)深度測(cè)試失敗(由于A的阻擋),所以模板緩沖值減1,所以最后結(jié)果是對(duì)于物體A,起模板緩沖中的值為0.
物體B:渲染陰影體的背面時(shí)深度測(cè)試失敗(由于B的阻擋),所以模板緩沖值加1.渲染陰影體的前面時(shí)深度測(cè)試成功所以最后結(jié)果是對(duì)于物體B,起模板緩沖中的值為1.
物體C:渲染陰影體的背面時(shí)深度測(cè)試成功,渲染陰影體的前面時(shí)深度測(cè)試也成功所以最后結(jié)果是對(duì)于物體C,起模板緩沖中的值為0.
請(qǐng)注意,到目前為止,我們還沒(méi)有碰到色彩緩沖區(qū)。 當(dāng)我們完成上述所有的操作后,我們?cè)俅问褂脴?biāo)準(zhǔn)的照明著色器渲染所有的對(duì)象,但是這次我們?cè)O(shè)置模板測(cè)試,使得只有模板值為零的像素才會(huì)被渲染。 這意味著只有對(duì)象A&C才能使其在屏幕上顯示出來(lái)。
讓我們看看如何把這個(gè)知識(shí)付諸實(shí)踐。 正如我們前面所說(shuō),我們需要渲染當(dāng)我們擴(kuò)展遮擋物的輪廓時(shí)創(chuàng)建的體積。 我們所需要做的就是將輪廓邊緣延伸到一個(gè)體積中, 這是通過(guò)為每個(gè)輪廓邊緣從GS發(fā)射四(或?qū)嶋H上四角形拓?fù)渲械乃膫€(gè)頂點(diǎn))來(lái)完成的。 前兩個(gè)頂點(diǎn)來(lái)自剪影邊緣,當(dāng)我們沿著從光照位置到頂點(diǎn)的向量將邊緣頂點(diǎn)延伸到無(wú)窮大時(shí),生成其他兩個(gè)頂點(diǎn)。 通過(guò)延伸到無(wú)限遠(yuǎn),我們確保體積捕獲位于陰影路徑中的所有物體。 這個(gè)四邊形如下圖所示:
?
?
當(dāng)我們重復(fù)這個(gè)從所有輪廓邊緣發(fā)射四邊形的過(guò)程時(shí),會(huì)創(chuàng)建一個(gè)體積。 夠了嗎? 當(dāng)然不。 問(wèn)題是這個(gè)體積看起來(lái)像一個(gè)沒(méi)有蓋子的截錐體。 由于我們的算法依賴于檢查體積的前后三角形的深度測(cè)試,所以我們可能會(huì)遇到一個(gè)情況,即從眼睛到像素的矢量可能沒(méi)有通過(guò)正面或背面,如下圖這個(gè)狀況:
解決這個(gè)問(wèn)題的方法是生成一個(gè)在兩邊封閉的體積。 這是通過(guò)創(chuàng)建一個(gè)正面和后面到體積(上圖中的虛線)完成的。 創(chuàng)建前蓋非常容易。 面向光的每個(gè)三角形都成為前蓋的一部分。后蓋需要將面向三角形的光的頂點(diǎn)延伸到無(wú)限遠(yuǎn)(沿著從矢量到每個(gè)頂點(diǎn))并反轉(zhuǎn)它們的順序(否則所得到的三角形將指向體積內(nèi))。“無(wú)限”一詞在這里已經(jīng)提到過(guò)幾次,我們現(xiàn)在需要確切地說(shuō)明這是什么意思。 看看下面的圖片:
我們看到的是從上面取出的截頭錐體的圖片, 燈泡發(fā)出一個(gè)穿過(guò)點(diǎn)p并繼續(xù)無(wú)限遠(yuǎn)的光線。 換句話說(shuō),p擴(kuò)展到無(wú)限遠(yuǎn)。 顯然,在無(wú)窮遠(yuǎn)處,點(diǎn)p的位置是簡(jiǎn)單的(無(wú)窮大,無(wú)窮大,無(wú)窮大),但是我們不在乎。 我們需要找到一種光柵化陰影體積的三角形的方法,這意味著我們必須在投影平面上投影其頂點(diǎn)。 實(shí)際上這個(gè)投影平面是近平面。 雖然p沿著光矢量延伸到無(wú)窮遠(yuǎn),但我們?nèi)匀豢梢栽诮矫嫔贤渡渌?這是通過(guò)從原點(diǎn)開(kāi)始的虛線完成的,并在某處穿過(guò)光矢量。 我們要找到Xp,它是該矢量穿過(guò)近平面的點(diǎn)的X值。
我們將光矢量上的任何點(diǎn)描述為p + vt,其中v是從光源到點(diǎn)p的向量,t是從0到無(wú)窮大的標(biāo)量。 從上圖和三角相似之處可以看出:
?
其中n是近平面的Z值。 隨著t到無(wú)窮大,化簡(jiǎn)公式如下所示:
所以這就是我們?cè)诮矫嫔先绾握业健盁o(wú)限遠(yuǎn)”的投影,根據(jù)上述我們只需要乘以矢量(Vx,Vy,Vz,0)(其中V是從光源到矢量的點(diǎn)p的向量)來(lái)計(jì)算通過(guò)視圖/投影矩陣并應(yīng)用透視分割、
?
總結(jié)一下 Z-fail算法(John Carmack's Reverse)
1. 先關(guān)閉光源,將整個(gè)scence渲染一遍,獲得深度值
2. 關(guān)閉深度寫(xiě),渲染陰影體的背面,深度測(cè)試失敗則模板值加1
3. 渲染陰影體的正面,深度測(cè)試失敗則模板值減1
4. 最后模板值不為0的面便處于陰影體中,開(kāi)啟深度寫(xiě)
5. 用模板手法重新渲染一次加光的scence即可,陰影部分不渲染色度
6. 注意,該算法要求陰影體積是閉合的,即需要前后封口
7. 該方法不是沒(méi)有缺陷的,有可能因?yàn)閆-far clip plane過(guò)近而導(dǎo)致模板計(jì)數(shù)錯(cuò)誤
?
陰影體渲染要點(diǎn)總結(jié)
- 輪廓檢測(cè)創(chuàng)建陰影體,封閉的面(但是在GEM這篇文章當(dāng)時(shí),輪廓檢測(cè)一般只能運(yùn)行在cpu中)
- Z-fail算法模板計(jì)數(shù)
- 渲染場(chǎng)景
?
?
總結(jié)
以上是生活随笔為你收集整理的GPU Gems1 - 9 有效的阴影体渲染的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: GPU Gems1 - 8 衍射的模拟
- 下一篇: Java中抽象类的构造器的作用