1. 一些理論知識
快速簡要地介紹一些理論知識.
光打到粒子上之後的效果:
光在介質上的散射,入射光分為吸收,透射,外散射三個部分,根據能量守恆,入射光等於三項的和
當不考慮中間介質時候,攝影機中得到的顏色就是最近的點上出射光的顏色
不考慮散射
考慮散射時,情況就比較複雜了:
光的散射
內散射會使路徑上的光增多,外散射會使路徑上的光變少.通常情況下,為了計算方便,不考慮多次散射的光.
模擬真實光穿透介質的比例的函數:
不同的散射類型:
Mie散射和Rayleigh散射
Rayleigh散射模型用來描述較小的粒子的散射,比如空氣粒子的散射使天空變藍. Mie散射模型是用來描述較大粒子的散射,比如灰塵.
Phase Function 是用來描述光在不同方向的散射分布的函數,根據能量守恆,光在所有方向散射的和總是1
Henyey–Greenstein Phase Function 是一個經常用來描述Mie散射的Phase Function
2. DepthFog
在每個像素計算的正常顏色基礎上,根據距離混合一個霧的顏色.
最常用的三種計算霧強度的公式:
Linear : factor = (end-z)/(end-start)
Exp: factor = exp(-density*z)
Exp2: factor = exp(-(density*z)^2)
unity中的全局霧設置和宏APPLY_FOG就是使用這種方式
unity中的全局霧設置
早期的Opengl和DirectX甚至提供的類似的API接口(glFog 等),在硬體級別實現霧效.
3. PostProcessing Fog
和Depth Fog基本上是一樣的,區別在於PostProcessing 的Fog通過在PostProcessing時根據depth texture反推計算出攝影機到目標像素點的距離.
除了用距離做參數,還可以根據高度作為參數,或者高度*距離這樣的形式來作為參數.
4. 粒子/BillBoard
在需要霧效的位置使用大量的粒子或者BillBoard來模擬光散射效果或者霧效.很顯然,這種方式很可能造成一些OverDraw性能問題,以及需要特定的觀察角度,而且不能隨光照變化.
5. Sun Shaft
也叫God Ray,通過PostProcessing來實現.GPU Gems 3 13.3中詳細描述了這種方法的實現.
原理是在PostProcessing時,將遊戲界面中的太陽以太陽為中心向外做一個Vector Blur + Bloom,再進行疊加,產生太陽光向外發散的感覺.當然了,這種方式只有太陽出現在畫面中的時候才有效果.
UE4中的Light Shafts包含兩種模式Light Shaft Occlusion和Light Shaft Bloom,後者就是上面所說的方式,前者則是將太陽周圍的暗部拉伸,產生遮擋的效果,原理是類似的.
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,得到平滑的結果.
有一個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.
沿著攝影機方向垂直切分
2.渲染光源的Depth Map,為了減少大量PCF陰影的性能消耗,這裡用ESM陰影,並進行DownSample.
3.使用Computer Shader,向Volume Texture中寫入空間中點的光照信息,光照包含空間環境光,光源光照,求和後寫入到Texture RGB通道中.空間點的散射強度值,寫入到Texture A通道中.
4.使用Computer Shader,從攝影機方向向外進行RayMarching,使用前面提到的模擬散射穿透計算的公式,累加光照.
5.渲染某個點時,在VolumeTexture中查找相應點的散射光照值,進行計算.
UE4中的VolumeRendeing就是這種方法.而且UE4還進行了升級,支持將Volume Lighting烘培到LightMass中.新版的UE4還有Volume Material和3DTexture導入,可以方便地實現各種效果.
Unity的HDRP同樣是這種方式渲染.Unity官方以前的宣傳片Adam,同樣是這種方法,可以參考:https://github.com/Unity-Technologies/VolumetricLighting