曾曉一, 何援軍
(上海交通大學計算機科學與工程系,上海 200240)
在現(xiàn)實世界中,陰影無處不在。陰影提供了光源對物體的照射信息,它增加了人們理解三維場景的深度[1]。對于陰影的生成,目前人們已經(jīng)做許多研究,現(xiàn)有的陰影算法分為兩大類:硬陰影算法和軟陰影算法[2]。所謂硬陰影算法是指場景中的點只有在陰影中或在陰影之外兩種狀態(tài),這種陰影是假設在只有理論上的“點光源”照射下的。但在現(xiàn)實場景中光源是有一定尺寸的,這使得場景中點有可能接受到部分的光源。在軟陰影算法中,定義一個點位于本影中,如果光源射向它的光線被全部遮擋了;否則,如果只有部分被遮擋,則這個點位于半影中。軟陰影比硬陰影有更多的真實感,但也更難以實現(xiàn)。如何有效簡便地生成具有真實感的實時軟陰影[3]是目前研究的焦點,也是本文要解決的問題。
目前兩種流行的陰影生成算法是陰影體(Shadow Volumes)[4-5]和陰影映射算法(Shadow Mapping)[6-7]。
Crow(1977)[4]提出的陰影體算法從光源位置將遮擋物的輪廓線沿著光源方向延伸,形成一個筒形的半無窮區(qū)域,稱這個區(qū)域為陰影體,處在陰影體內(nèi)的點繪制為陰影。使用模板緩沖能夠很方便地實現(xiàn)該算法。該算法生成的是精確的硬陰影,不會產(chǎn)生鋸齒現(xiàn)象,但是對場景中幾何對象有限制,非常依賴幾何形體,沒有很好的魯棒性。
相對于陰影體算法,Lance Williams(1978)[6]提出的陰影映射算法是一種基于圖像采樣技術的陰影生成算法,對場景中的幾何物體沒有特別的要求,但卻存在比較嚴重的走樣現(xiàn)象[8]。
普通的陰影映射算法需要兩步繪制過程:
(1)以光源位置為視點計算畫面 將視圖上各像素點的深度值保存在深度緩沖紋理中,即“陰影映射圖”中。
(2)回到觀察者視點位置計算畫面 使用上一步得到的深度紋理來進行陰影測試,通過比較給定點在光源方向視圖下的深度值與該點在“陰影映射圖”中的深度值關系來判斷該點是否位于陰影中。如果給定點和光源之間沒有任何遮擋物體,則該給定點在光源方向視圖下的深度值與該點在“陰影映射圖”中的深度值應該相等,則判斷該點不在陰影中;反之則判斷該點在陰影中。
這樣的兩步計算過程會由于“陰影映射圖”的精度和采樣問題使生成的陰影出現(xiàn)嚴重的走樣現(xiàn)象。
為了解決普通陰影映射算法的走樣問題,本文提出的陰影算法并不是直接通過比較給定點在光源方向視圖下的深度值與該點在“陰影映射圖”中的深度來直接決定一個點是否在陰影中,而是結合給定點周圍點的信息來決定此點是否在陰影中。因此,在普通算法第二步前,并不直接利用陰影映射圖將場景繪制到屏幕,而是使用陰影映射圖先將場景繪制到一個并不立即顯示的屏幕緩沖紋理中,然后對此紋理進行一些處理,最后再利用此處理后屏幕緩沖紋理的將場景繪制到屏幕。
本文提出的軟陰影算法包含下列步驟:
(1)以光源位置為視點,將場景的深度信息渲染到浮點格式的紋理中,生成陰影映射圖。
(2)回到觀察點,關閉所有光源,利用陰影映射圖將場景的陰影信息渲染到屏幕緩沖紋理中。
(3)對上一步中得到的屏幕緩沖紋理進行反走樣處理和模糊處理。
(4)打開所有光源,利用處理后的屏幕緩沖紋理將場景渲染到屏幕。
算法第一步和普通的陰影映射算法一樣,首先創(chuàng)建一個浮點格式的紋理作為render target,然后以光源位置為視點,將場景的深度信息渲染到這個紋理中,生成陰影映射圖(Shadow map)。由于深度信息值的范圍很大,因此創(chuàng)建一個 R32F浮點格式類型的紋理來保存陰影映射圖。
算法第二步和普通的陰影映射算法的基本原理是一樣的,首先通過深度的比較來判斷每個像素點是否在陰影中,得到該點的陰影系數(shù)。不同的是并沒有直接利用這陰影系數(shù)將場景渲染到屏幕,而是將場景中每個像素的陰影系數(shù)信息渲染到一個并不立即顯示的紋理緩沖中。屏幕上的像素點如果在陰影中,則將這點的陰影系數(shù)設置成0.1,如果不在則設置成1.0。這個帶有陰影信息的紋理將在下一步進行處理。
為了得到比較真實的軟陰影效果,本算法第三步將對上一步得到的紋理進行反走樣和模糊處理。處理反走樣,本算法采用percentage closer filtering (PCF)來平滑鋸齒邊緣,即簡單的對每個點周圍8個紋理點進行采樣,并取得它們陰影系數(shù)的平均值作為該點的陰影系數(shù)。對于模糊處理,本算法使用Gaussian filter高斯濾波來模糊屏幕緩沖紋理,即對每個像素點周圍的點進行采樣,并用這些采樣點的高斯加權均值來設置該點的值。
利用處理后的屏幕緩沖紋理,將場景渲染到屏幕,由于這時的屏幕緩沖紋理已經(jīng)經(jīng)過了前面一步的反走樣和模糊處理,最后將得到比較柔和的陰影效果。
在最后場景的繪制中,本算法使用了如下局部光照模型

Id為漫反射光強,Ia為環(huán)境光光強,Is為鏡面發(fā)射光強,fShadowTerm為陰影系數(shù),如果像素點在陰影中則fShadowTerm為0,不在則為1。但在實驗中發(fā)現(xiàn),按照上面的公式進行繪制將導致那些處于陰影中的像素全黑,這與真實世界并不一致。在真實世界中,由于環(huán)境光和周圍物體的散射,處于陰影中的像素并沒有完全被遮蔽,因此為了得到比較真實的陰影效果,本算法將光照模型做了如下調(diào)整:

并對陰影系數(shù)fShadowTerm的設置做了些調(diào)整,如果像素點在陰影中則fShadowTerm為0.1,不在則為1.0。
因為陰影映射圖中存儲的深度值是精度有限的浮點數(shù),所以存儲在陰影映射圖中的各像素點的深度值可能和該點的實際深度值有誤差。這個誤差會影響到算法第二步判斷該點是否是陰影點。如圖1所示,對于茶壺上和基面上的一些本不該處在陰影中的點,這些點和光源之間沒有任何遮擋物體,所以這些點在光源方向視圖下的深度值與它們在“陰影映射圖”中的深度值應該相等,則在算法第二步進行深度比較的時候應該判斷這些點不在陰影中。但由于存儲在陰影映射圖中的深度值只是一個有限精度的浮點數(shù),造成將實際上相等的兩個深度值判斷成不等,從而將這些點錯誤地判斷為陰影點,即形成了如圖1所示的波紋。

圖1 精度問題
為解決這個問題,本算法在第二步比較深度值的時候,減去了一個很小的浮點數(shù)SHADOW_EPSILON,實驗表明將SHADOW_EPSILON設置成 0.001f是比較合適的值。

陰影映射算法還和所有紋理相關技術一樣,面臨著共同問題:走樣。走樣問題將造成陰影邊界出現(xiàn)鋸齒的現(xiàn)象[9]。具體分析原因,陰影圖是在光源視圖中生成的,它生成過程實際上是一個從光源對場景逐像素采樣的過程,它把場景中的每個采樣點的深度信息作為一個像素保存在圖中,當視平面中的像素轉換到陰影表中進行比較時,肯定會出現(xiàn)轉換過去的像素比陰影圖的像素要大些或者小些的現(xiàn)象。如果大了,可以平均它所對應的陰影圖的像素值來進行比較,確定一個適當?shù)闹担蝗绻⌒敲磶讉€在視平面的像素會對應同一個在陰影圖中的像素,那就造成多個像素獲得同樣的比較結果,這樣就造成了陰影圖的采樣精度低于視平面的精度而出現(xiàn)的走樣問題。圖3顯示了使用128*128,256*256,512*512不同精確度陰影映射圖的陰影效果,可以看出走樣問題會隨著陰影映射圖精確度的提高而減小。但是這個代價是非常高的,尤其是在實時動態(tài)場景中,內(nèi)存空間是非常有限的。
除了提高陰影映射圖的分辨率,本算法還在第三步對陰影映射圖做了 percentage closer filtering (PCF)處理,來平滑鋸齒邊緣。
為了得到比較柔和的軟陰影,算法第四步使用Gaussian filter高斯濾波來模糊屏幕緩沖紋理,即用像素點周圍點的高斯加權均值來設置該點的值。實驗中使用了兩種方法來處理:第一種是對每個像素點左右的各7個像素點,共15個點進行采樣,并依照一維高斯分布來設置采樣點的權值,即對陰影圖橫向模糊處理一遍,然后同樣對每個像素點上下的各7個像素點,共15個點進行采樣,即縱向模糊處理一遍;第二種是對每個像素點左右上下的7*7的方格中共49個點進行采樣,并依照二維高斯分布來設置采樣點的權值。實驗顯示,這兩種策略的效果差不多,但第二種方法的計算量相對較大,而且也不利于shader程序的編寫,所以本算法使用第一種方法,即橫縱兩遍模糊處理。
本算法的實現(xiàn)平臺為DirectX9.0 VS2005,實驗機器配置為:CPU AMD Athlon 64 Dual Core 2.21 GHz ,內(nèi)存 1.00 GB,顯卡 NVIDIA GeForce7500 LE,操作系統(tǒng)Windows XP。
實驗場景包含兩個物體和一個光源,兩物體為一個正四邊型的基面和一個茶壺,光源是方向為(-1.0f,-0.8f,0.0f )的方向光源。
首先以光源位置為視點,將場景的深度信息渲染到浮點格式的紋理中,生成的如圖2所示的陰影映射圖。灰度越小(顏色越深),表示該像素點的深度越小;灰度越大(顏色越淺),該像素點深度的越大。

圖2 場景的陰影映射圖

圖3 采用不同分辨率的陰影效果圖

圖4 三種陰影效果圖比較
使用圖2的512*512分辨率的陰影映射圖,繪制得到如圖4所示的三幅陰影圖。第一幅為普通的硬陰影圖,第二幅為經(jīng)過 percentage closer filtering (PCF) 邊緣鋸齒平滑處理后的陰影圖,最后一幅為經(jīng)過高斯模糊處理后的軟陰影圖。
從第一幅圖中可以看到,雖然已經(jīng)使用了高分辨率的陰影映射圖,但陰影邊緣還是有鋸齒現(xiàn)象,可見僅靠提高分辨率是無法完全消除鋸齒的。第二幅圖做了PCF處理,鋸齒現(xiàn)象明顯有所改善。最后一幅圖通過模糊化處理,陰影已經(jīng)比較柔和了。
以上三種陰影的FPS(幀/每秒),見表1所示。

表1 三種陰影的FPS(幀/每秒)
可見本算法實現(xiàn)的軟陰影有較好的實時性。
基于陰影映射圖算法,提出了一個有效的實時軟陰影算法。本算法采用了 percentage closer filtering (PCF)來平滑鋸齒邊緣,有效的消除了陰影邊緣的走樣現(xiàn)象,并使用高斯模糊處理得到了較好的軟陰影效果。實驗結果表明,本算法不僅簡單實用,利于編程,并且也有較好的實時性,可適應實時動態(tài)變化場景的需求。
本算法在處理走樣問題上仍有可改進的地方,在生成陰影映射圖的時候,只需要在可能產(chǎn)生鋸齒的地方調(diào)高采樣率,比如陰影邊緣提高采樣率,這樣可以減少陰影映射圖的內(nèi)存開銷。
[1]何援軍. 計算機圖形學[M]. 北京: 機械工業(yè)出版社,2006. 197-198.
[2]Andrew Woo, Pierre Poulin, and Alain Fournier A. A survey of shadow algorithms [J]. IEEE CG&A, 1990,10(6): 13-31.
[3]Jean-Marc Hasenfratz, Marc Lapierre, Nicolas Holzschuch. A survey of real-time soft shadows algorithms [J]. Computer Graphics, 2003, 22(4):753-774.
[4]Crow F. Shadow algorithms for computer graphics [J].Computer Graphics (Proc. SIGGRAPH), 1977, 11(3):2-7.
[5]Bergeron P. A general version of Crow’s shadow volumes [J]. IEEE Computer Graphics& Applications,1986, 6(9): 17-28.
[6]Lance Williams. Casting curved shadows on curved surfaces [C]//Proceedings of SIGGRAPH’78, 1978:270-274.
[7]Lokovic T, Veach E. Deep shadow maps [M]. Pixar.Acm Siggraph, Addison-Wesley, 2000. 2-8.
[8]HOURCADE J C, NICOLAS A. Algorithms for antialiased cast shadows [J]. Computer Graphics,1985, 9(3): 259-265.
[9]REEVES W T, SALESIN D H, COOK R L.Rendering antialiased shadows with depth maps [C]//Computer Graphics (Proceedings of ACM SIGGRAPH 87), 1987: 283-291.