大約1年前,筆者在Gamasutra上發(fā)表了《游戲領(lǐng)域的光線跟蹤實用技巧》一文,闡釋了開發(fā)人員如何在PowerVR GR6500(即我們的光線可跟蹤GPU)上應用一系列的混合渲染技術(shù)來實現(xiàn)一些令人印象深刻的效果。盡管光線跟蹤的目標應用程序極為不同,但本文主要關(guān)注點在于陰影。光線跟蹤不僅僅可以創(chuàng)造更多精確的且可擺脫陰影貼圖偽影的陰影,其跟蹤陰影的效率還成倍地提高了。它們只需GPU一半的周期或更多的是只需一半的內(nèi)存?zhèn)鬏敱憧缮筛呖杀刃曰蛸|(zhì)量更好的圖像。 接下來,本文將帶你走進一個高效實現(xiàn)軟陰影技術(shù)的全過程。 傳統(tǒng)柵格圖像中的級聯(lián)陰影貼圖 首先,讓我們回顧一下級聯(lián)陰影貼圖——即當下使用的為柵格圖形生成陰影的最先進的技術(shù)。級聯(lián)陰影貼圖旨在基于視點的距離將視景體分為許多區(qū)域,并在每個區(qū)域呈現(xiàn)一個陰影。這為陰影貼圖提供了可變的分辨率:距離相機較近的物體則可得到高分辨率,而距離較遠的物體每單位面積分辨率則更低。 下述圖中,我們可以看到場景中一些物體的示例。陰影貼圖將逐一呈現(xiàn),且每個貼圖覆蓋的場景將逐漸變大。由于所有的陰影貼圖都具有相同的分辨率,因此陰影貼圖像素的密度將隨著我們逐漸遠離視點而變得越來越低。 最后,在最終場景中使用相機的視角再度呈現(xiàn)物體時,我們將基于每個物體與視點的距離來篩選恰當?shù)年幱百N圖,并在陰影貼圖之間進行插補,以確定最終的像素是亮或暗。 這樣的復雜性只為一個目的:減少由于陰影貼圖過粗導致分辨率低這類情況的發(fā)生。這是由于物體離視點太遠,在場景中占有的空間小,因此所需的陰影細節(jié)少。盡管GPU周期和內(nèi)存?zhèn)鬏數(shù)某杀竞芨撸@樣處理卻非常好。 光線跟蹤陰影介紹 從根本上說,光線跟蹤陰影是在屏幕空間上運作。因此,沒必要將陰影貼圖的分辨率與屏幕分辨率進行匹配,分辨率問題由此也不存在。 基本的光線跟蹤陰影算法如下:對于可見表面的每一個點,我們將直接朝光源的方向發(fā)出射線。如果光線到達了光源,其表面便是明亮的,我們使用的便是傳統(tǒng)的常規(guī)照明程序。如果光線在到達光源之前遇到了其它障礙物,那么,由于表面處在陰影中,光線將被忽略。這項技術(shù)生成的是硬陰影,正如我們在萬里無云的晴天所見的影子一樣。 然而,現(xiàn)實世界中的很多陰影都在更亮和更暗的區(qū)域之間漸進過渡——這種軟邊緣被稱為半影。半影是由多個光物理相關(guān)的因素引起。盡管很多游戲?qū)⒐庠茨M為無量綱點源,而實際上,每個光源都有一個表面。表面區(qū)域可使陰影柔化。在半影區(qū)域內(nèi),部分光被遮擋物阻塞,但剩下的光則有一條清晰的線路。這就是為何看到的區(qū)域并不是完全明亮也不是完全處在陰影中。 下圖展示了如何基于以下三個變量來計算半影的大小:光源的大小(R)、光源的距離(L)、遮擋物和陰影投射表面之間的距離(O)。將遮擋物逐漸靠近表面時,半影也在縮小。 基于這三種變量,我們可以得出一個簡單的等式來計算半影的尺寸。 使用這個簡單的關(guān)系,我們可以制定一個算法。在這個算法中,每像素僅使用一條光線來呈現(xiàn)精確的軟陰影。可以從上述的硬陰影算法入手,當射線與物體相交時,在屏幕緩沖區(qū)記錄下表面到物體的距離。 該算法可以擴展到半透明的表面。例如:穿過一個表面時,可以記錄是否該表明是透明的。如果表面是透明的,可以選擇繼續(xù)讓光線穿過表面(注意它在獨立密度緩沖區(qū)的alpha值)。 光線跟蹤陰影優(yōu)勢 相比級聯(lián)陰影貼圖或其它通用的技巧,該方法的優(yōu)勢在于: ·由于它是基于屏幕空間,因此不存在陰影貼圖分辨率的問題 ·由于抽樣誤差,因為沒有條帶、噪音或嗡嗡聲效果 ·由于直接幾何投射光線,因此不存在偏置問題(有時稱為Peter-Panning),陰影和投射物體之間也沒有任何接觸 以下展示了光線跟蹤通過的緩沖區(qū)(我們上傳的是原始的框架)。 第一、我們有光線跟蹤密度緩沖區(qū)。這個場景中的很多物體都是不透明的,因此陰影密度值為1。然而,圍欄區(qū)域包含了多個密度值在0和1之間的像素。 接下來便是到遮擋物緩沖區(qū)的距離。由于我們遠離遮擋物,紅色分量的值便增加了,這表明陰影像素和遮擋物之間的距離變大了。 最后,我們使用了一個濾波器來計算,使用這兩個緩沖區(qū)的每個像素其陰影值是多少。 首先,我們計算出對像素產(chǎn)生影響的半影的大小,使用這個半影來選擇一個模糊的內(nèi)核半徑,然后相應地將屏幕空間陰影密度緩沖區(qū)便模糊。對于在遠處遮擋物緩沖區(qū)具有密度值的像素,計算半影非常簡單。由于我們知道到遮擋物的距離,我們只需要將這個值從世界空間投射到屏幕空間,并使用投射的半影來選擇模糊的內(nèi)核半徑。 若像素是亮的,則需要進一步加強工作。我們采用了十字搜索算法來尋找另一個可形成半影的光線像素。如果在X軸或Y軸上發(fā)現(xiàn)任何處在陰影中的像素(即,具有有效的距離值),則選擇距離遮擋物最大的值,且使用其計算像素的半影區(qū)域。隨后,調(diào)整已找到的像素的梳理,計算出半影側(cè)邊。 從這里開始,算法都是相同的:將世界空間的半影大小投射到屏幕空間,然后算出半影所覆蓋的像素區(qū)域,最后進行模糊處理。如果沒有發(fā)現(xiàn)陰影像素,這種情況則假設像素完全是亮的。 下圖表示我們最后的濾波器內(nèi)核。 用方框濾波器盒覆蓋半影區(qū)域,以此取樣并同時注意不連續(xù)表面。這樣有助于得到深度抑制值,為計算深度抑制值,我們將使用本地差分來計算X軸和Y軸上當前像素和定值之間的delta關(guān)系。 有了計算結(jié)果,我們便知道在穿過屏幕空間時還需要后退多遠。對內(nèi)核抽樣時,我們將基于離中心像素的距離,擴大深度閥值。 結(jié)論:級聯(lián)陰影貼圖vs.光線跟蹤陰影 在上述示例中,我們已拒絕了所有標記為紅色的樣本,因為其相應的區(qū)域?qū)儆跂艡冢覀儎t專注對地上的一點進行采樣。進行模糊處理后,由此生成的緩沖區(qū)則表示對貫穿屏幕陰影密度的準確估計。 這是上文發(fā)布的原始框架進行對比的最終截圖。注意,圖像質(zhì)量的改善是由光線跟蹤陰影在級聯(lián)陰影貼圖的基礎上完成的。 |