體積陰影 Shadow Volumes 生成演算法

2021-09-05 15:46:09 字數 3291 閱讀 8149

這裡我們快速往前跳,perspective projection、depth test、stencil buffer等概念就不多談了。shadow volume的一般步驟為:生成陰影體積(mesh)和陰影渲染,陰影體積生成演算法又分兩種,一種是d3d sdk sample中所採用的方法,先分離/插補物體面,然後用vertex shader加速,對複製所得的未拉伸的陰影體積進行沿光線方向的extrusion;另一種則是多數tutorial中常用的software renderer方式,首先揀選出所有向光面,將每乙個向光面的所有邊均加入乙個list,每加入乙個邊時,如果list中已經存在相同的邊了,則不加入,同時將list中相同的邊刪除,這樣處理完所有的向光面後,list中便只會剩下物體向光面輪廓的邊資訊,將該輪廓沿光向量方向進行延伸,然後完成側面quad插補和前後封口(如果使用的是z-fail)即可。而對於陰影渲染,則一般用4個pass,具體有z-pass/z-fail兩種做法:

z-pass演算法

1. 先關閉光源,將整個scence渲染一遍,此時一片漆黑,但獲得了深度值

2. 關閉深度寫,渲染陰影體的正面,深度測試通過則模板值加1

3. 然後渲染陰影體的背面,深度測試通過則模板值減1

4. 最後模板值不為0的面就在陰影體中,開啟深度寫

5. 用模板手法重新渲染一次加光的scence即可,讓陰影部分為黑色

6. 其致命缺點是當視點在陰影中時,會導致模板計數錯誤

7. 同時,有可能因為z-near clip plane過近而導致模板計數錯誤

z-fail演算法(john carmack's reverse)

1. 先關閉光源,將整個scence渲染一遍,獲得深度值

2. 關閉深度寫,渲染陰影體的背面,深度測試失敗則模板值加1

3. 渲染陰影體的正面,深度測試失敗則模板值減1

4. 最後模板值不為0的面便處於陰影體中,開啟深度寫

5. 用模板手法重新渲染一次加光的scence即可,陰影部分不渲染色度

6. 注意,該演算法要求陰影體積是閉合的,即需要前後封口

7. 該方法不是沒有缺陷的,有可能因為z-far clip plane過近而導致模板計數錯誤

值得注意的方面

1. z-pass由於不用封口,因此速度比z-fail快,但存在處理不了的情況

2. quake 3貌似使用的是z-pass shadow volume和planar shadow

3. 為保證足夠robust,必須確保z-near/z-far中至少乙個不出問題,nvidia的**推薦採用z-fail,用w=0來實現無窮遠的z-far平面

4. 記住使用z-fail一定要封口,而且陰影體積的每個面的法線必須正確地指向物體之外,包括front cap和back cap

5. 上面給出的是簡單過程,陰影很硬,可以稍微變通一下:先以環境光渲染一遍,然後計算模板,再用模板渲染打光的scene,最後以alpha blend加深陰影

下面開始大面積地貼圖,首先來看個z-fail的具體例子(z-pass簡單,就不舉例了):從外面看乙個面向光源的單面的情況。圖中,虛線框起來的為陰影體積,圓形為點光源,其中,面adfb和麵def是back faces,而面abc、aced和cbfe則是三個front faces(注意這裡頂點順序用的是左手系),為說明問題(主要是為了說明在計算陰影體積時如何避免z-fighting),我故意把麵abc畫在了黃色三角形下面一點點的位置,而且其中的灰色陰影是與平面共面的,而面def則處於平面下面一點點的地方。

首先進行2個back faces的depth test:粉紅色的是stencil buffer中因z- fail而加1的區域;然後進行3個front faces的depth test:亮綠色的是stencil buffer中因z- fail而減1的區域

最後紅綠區域正負相抵,stencil buffer中只剩下平面上的灰色三角區域中的值不為0,即原圖陰影所在的位置。上面是個從單面外看的例子,值得注意的是,單面與兩個背靠背雙面的情況是不一樣的,那麼後者的計算結果是不是也是正常的呢?繼續看圖:

上圖的紅、綠細線分別表示兩個背靠背單面,而粗線則構成了體積陰影,紅粗線、綠粗線分別表示紅面、綠面沿光線方向的的投影,一般而言,back cap處於無窮遠處,而front cap則值得注意,為避免z-fighting,它的位置應該是在紅面之後、綠面之前,也許你會說:「hey, 這裡的front cap完全可以直接等於紅面嘛」,是的,當在eye看來紅面是背面時的確可以這麼做,可當eye位置變化、紅面不再是背面時,簡單的以紅面為front cap便會導致z-fighting。從圖上可看出,當eye處於陰影內部時,模板計數也是正確的。下面再來看看僅有乙個單面、且處於陰影內部的情況:

只要front cap注意了z-fighting的情況,其計算結果也是正確的,有意思的是,實際上當eye處於陰影內部時,在back culling的時候,三角形的背面便已經被去除了,因此在實際渲染時,在三角面的背面處並不會存在其深度資訊,但即便如此,z-fail的模板計數仍是正確的。

最後,再來看乙個計算方塊的陰影的例子,可以看出,當front cap為背面時,陰影體積的計算很簡單,無需考慮其z-fighting的情況,直接用方塊的向光面作為front cap即可;但當front cap為正面時,還是面臨著位置需要微調的問題。

總結一下,用z-fail方式來計算沒有厚度的面的陰影和具有厚度的物體的陰影還是有點微妙區別的;在計算陰影體積時,要注意避免front cap處z-fighting的情況,稍微調整一下front cap的位置,即將front cap沿光線方向稍微向後挪那麼一點點;或者是.... 等等,還有一種更簡潔優雅的辦法,即將z-fail演算法中的"depth test失敗"理解成象素深度">="所在位置的深度而不僅僅只是">",將zbufferfunction設為campare.less而不是campare.lessequal,這樣的話便完全避開了z-fighting的問題,微調什麼的都可以免了,無論是面還是物體,在任何情況下都可以直接用物體向光面作為front cap....faint...快寫完了才考慮清楚...白廢話了那麼多....無比鬱悶-_-b....收工.

體積陰影 Shadow Volumes 生成演算法

doom 3,不足在於陰影體積引入了額外的頂點和面,加大了儲存和處理強度,同時渲染出的陰影比較硬,如果要實現軟陰影,仍需其他技術配合。這裡我們快速往前跳,perspective projection depth test stencil buffer等概念就不多談了。shadow volume的一般...

體積陰影的生成演算法

doom 3,不足在於陰影體積引入了額外的頂點和面,加大了儲存和處理強度,同時渲染出的陰影比較硬,如果要實現軟陰影,仍需其他技術配合。這裡我們快速往前跳,perspective projection depth test stencil buffer等概念就不多談了。shadow volume的一般...

anroid邊框陰影 圓角陰影 漸變陰影

anroid給邊框加陰影只能對內陰影,沒有給邊框對外加陰影,在自定義shape中增加一層或多層,並錯開,即可 顯示陰影效果。給邊框加陰影可使用 1.圓角陰影效果 內陰影 不加漸變色,第一層漸變色其實沒啥用,是對整個邊框漸變,2dp的陰影還是乙個顏色。android centerx 0.5 andro...