上一回我們說到Cluster Based Lighting,將攝像機空間切成塊,使每一塊稜台都保持有一個自己的光照隊列,這樣的光照剔除方法有非常多的好處,其中最主要的好處在於其魯棒性高,比如既能支持Forward Pipeline,也可以支持Deferred Pipeline,無論是氣體渲染,液體渲染,透明固體渲染,粒子渲染等,都可以通過一個世界坐標獲取到當前的受光信息。但是僅僅如此是不夠的,因為Cluster Based Lighting也有其局限性,其中最致命的局限在於其光照剔除精度的不足。因為不依賴深度,所以在切割的時候必須將空間的Z軸切割成多段,譬如在MPipeline中,我們按照非線性近小遠大的規則將空間的Z軸切割成64份,因此在同樣的切割解析度下,其剔除消耗將會是TBDR的64倍。最終的結果就是光照的剔除精度必須做妥協,因此我們的XY軸切割解析度就大大的降低了,降低到了16 * 8。很顯然,將屏幕橫豎分為16 * 8這樣的剔除精度是遠遠不夠的,因此我們需要專門為光照運算的主要部分,也就是延遲管線渲染的物體做一個單獨的TBDR。

光照的剔除方法一般要考慮需求場景的目標光源的大致數量,而我們這裡場景管理考慮的是,在到攝像機裁面的一定距離內,如128米,192米或256米等,使用實時的燈光,即支持陰影,高光,物理衰減等高級光照運算。而超出一定距離後則使用LOD的切換,換成Light Volume直接使用GBuffer進行疊色,據悉GTAV使用的光照計算方法就是這樣的:

在這種情況下,Tile/Cluster負責的光照一般不會特別多,我們的設計預估量是在100個實時光源以下,所以在實現TBDR時並沒有使用四叉樹分割演算法,而是直接暴力的遍歷所有光照頂點。

在剔除精度上,我們使用屏幕的1/16解析度作為Tile Resolution,如筆者當前使用的平台為2560 * 1440解析度,則最終會分割出一個160 * 90個Tile。

理論知識講到這裡,我們開始實踐,首先第一步是生成一個降採樣過的Depth Bounding Map,通俗的來講就是這16 * 16個像素的最大的深度和最小的深度,並將這兩個值保存到一個RGHalf格式的RenderTexture中,而這裡將最小值儲存在R通道,最大值儲存在G通道,最終得到的結果如下:

這張圖表達的結果一目了然,可以看到總體色彩是黃色,這說明深度相對連續,而綠色部分則說明深度比較不連續,即R值較小而G值較大。有了這張深度圖,我們就可以開始光照的剔除運算部分了。

光照的剔除運算部分和之前的Cluster Rendering並無太大區別,都是算出Bounding Box在世界坐標的6個平面,並使用這6個平面與椎體,球體進行碰撞檢測,下方是獲取6個面的代碼:

在剔除部分,我們在腳本里創建了兩個RInt格式的Texture3D,一個負責點光源一個負責錐光源,XY軸對應每個Tile的坐標,而Z軸則給了64個燈光索引的位置以及頭上的數量標籤,也就是65個位置,這樣剔除後直接將結果塞進圖中:

接下來在Shader中,按照屏幕UV直接讀取光照信息:

做到這裡,整條TBDR基本實現,這個是最簡單最粗暴的實現方法,本人也只用了一小時多不到兩小時的時間就完成了,在場景中擺兩個光源,一個點光一個聚光,先輸出一下剔除結果,R通道表示點光覆蓋,G通道表示聚光覆蓋:

光照渲染的結果:

結果看起來非常正確,而性能也是非常高的,同屏70+盞燈:

本篇文章就到這裡,最後祝大家身體健康,再見。

推薦閱讀:

相关文章