遊戲中的Volumetric Rendering

遊資網發表於2020-03-31
遊戲中的Volumetric Rendering

總結下游戲中常見的Volumetric Rendering的實現方法

1. 一些理論知識

快速簡要地介紹一些理論知識.

光打到粒子上之後的效果:

遊戲中的Volumetric Rendering

光在介質上的散射,入射光分為吸收,透射,外散射三個部分,根據能量守恆,入射光等於三項的和

當不考慮中間介質時候,攝影機中得到的顏色就是最近的點上出射光的顏色

遊戲中的Volumetric Rendering
不考慮散射

考慮散射時,情況就比較複雜了:

遊戲中的Volumetric Rendering
光的散射

內散射會使路徑上的光增多,外散射會使路徑上的光變少.通常情況下,為了計算方便,不考慮多次散射的光.

模擬真實光穿透介質的比例的函式:

遊戲中的Volumetric Rendering

不同的散射型別:

遊戲中的Volumetric Rendering
Mie散射和Rayleigh散射

Rayleigh散射模型用來描述較小的粒子的散射,比如空氣粒子的散射使天空變藍. Mie散射模型是用來描述較大粒子的散射,比如灰塵.

Phase Function 是用來描述光在不同方向的散射分佈的函式,根據能量守恆,光在所有方向散射的和總是1

遊戲中的Volumetric Rendering

Henyey–Greenstein Phase Function 是一個經常用來描述Mie散射的Phase Function

遊戲中的Volumetric Rendering

2. DepthFog

在每個畫素計算的正常顏色基礎上,根據距離混合一個霧的顏色.

最常用的三種計算霧強度的公式:

Linear : factor = (end-z)/(end-start)

Exp: factor = exp(-density*z)

Exp2: factor = exp(-(density*z)^2)

unity中的全域性霧設定和巨集APPLY_FOG就是使用這種方式

遊戲中的Volumetric Rendering
unity中的全域性霧設定

早期的Opengl和DirectX甚至提供的類似的API介面(glFog 等),在硬體級別實現霧效.

3. PostProcessing Fog

和Depth Fog基本上是一樣的,區別在於PostProcessing 的Fog通過在PostProcessing時根據depth texture反推計算出攝影機到目標畫素點的距離.

除了用距離做引數,還可以根據高度作為引數,或者高度*距離這樣的形式來作為引數.

4. 粒子/BillBoard

在需要霧效的位置使用大量的粒子或者BillBoard來模擬光散射效果或者霧效.很顯然,這種方式很可能造成一些OverDraw效能問題,以及需要特定的觀察角度,而且不能隨光照變化.

遊戲中的Volumetric Rendering

5. Sun Shaft

也叫God Ray,通過PostProcessing來實現.GPU Gems 3 13.3中詳細描述了這種方法的實現.

原理是在PostProcessing時,將遊戲介面中的太陽以太陽為中心向外做一個Vector Blur + Bloom,再進行疊加,產生太陽光向外發散的感覺.當然了,這種方式只有太陽出現在畫面中的時候才有效果.

遊戲中的Volumetric Rendering

UE4中的Light Shafts包含兩種模式Light Shaft Occlusion和Light Shaft Bloom,後者就是上面所說的方式,前者則是將太陽周圍的暗部拉伸,產生遮擋的效果,原理是類似的.

遊戲中的Volumetric Rendering
UE4中的Light Shaft設定

Unity早期的Image Effects中,也有Sun Shaft效果元件.

6. RayMarching

使用PostProcessing和RayMarching來實現,GPU PRO5裡有一篇文章Volumetric Light Effects in Killzone Shadow Fall by Nathan Vos詳細介紹了這種方法.

基本原理是在pre-z得到場景Depth Buffer以及光照的Shadow Map後,確定一個要進行Volume Light的區域(點光源球形,錐形光錐形,直線光是全屏的),RayMarching計算出一個Volume Light的Buffer,計算光照效果時應用效果.

生成Volume Rendering Buffer的過程其實就是在Volume Redering區域中進行RayMarching,為了降低Artfict,通常會使用一個Dither Texture進行抖動,並且在最後對結果進行Blur,得到平滑的結果.

遊戲中的Volumetric Rendering

有一個Unity的Volume Lighting的Demo是這種方式來實現的, https://github.com/SlightlyMad/VolumetricLights.

7. Volume Texture

使用Volume Texture來儲存空間中的散射資訊,是真正意義上的Volume Rendering.

一個詳細描述了實現方法的文章: https://www.ea.com/frostbite/new ... ering-in-frostbite.

一個簡單的Volume Texture實現的Volume Rendering的過程:

1.劃分VolumeTexture的區域,這裡將攝影機視錐內的部分作為Volume Texture區域,沿著垂直於攝影機方向的平面切分.為了較好的效能,將3DTexture的大小弄的小一點,比如180x60x64.

遊戲中的Volumetric Rendering
沿著攝影機方向垂直切分

2.渲染光源的Depth Map,為了減少大量PCF陰影的效能消耗,這裡用ESM陰影,並進行DownSample.

3.使用Computer Shader,向Volume Texture中寫入空間中點的光照資訊,光照包含空間環境光,光源光照,求和後寫入到Texture RGB通道中.空間點的散射強度值,寫入到Texture A通道中.

4.使用Computer Shader,從攝影機方向向外進行RayMarching,使用前面提到的模擬散射穿透計算的公式,累加光照.

遊戲中的Volumetric Rendering

5.渲染某個點時,在VolumeTexture中查詢相應點的散射光照值,進行計算.

UE4中的VolumeRendeing就是這種方法.而且UE4還進行了升級,支援將Volume Lighting烘培到LightMass中.新版的UE4還有Volume Material和3DTexture匯入,可以方便地實現各種效果.

Unity的HDRP同樣是這種方式渲染.Unity官方以前的宣傳片Adam,同樣是這種方法,可以參考:https://github.com/Unity-Technologies/VolumetricLighting


作者:TC130
專欄地址:https://zhuanlan.zhihu.com/p/102114679

相關文章