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