Unity体积光实现浅析

一、特效方式实现

1.1 Billboard特效片实现

采用这种方式的体积光实现简单,性能好,但是对于角度有特殊的要求,只适合某些场景使用

1.2 ShadowGun实现

ShadowGun2011年的一个使用Unity开发的移动平台的第三人称射击游戏

ShadowGun中把体积光归于是雾的一种应用

和体积光有非常多类似的地方,一个大面积的体积光从视觉上来看和雾非常像,它们有一个共性的感性认识,就是其可见度和距离视角的远近有关

因此ShadowGun用简单网格+Alpha Blending的方法来模拟雾和体积光

一旦玩家离网格面太近,它就会淡出,而且网格面的顶点会被拉开(因为即使是完全透明的Alpha曲面仍会消耗大量渲染时间)。

顶点颜色 alpha 决定哪些顶点是可移动的,哪些顶点是不可移动的(在我们的示例中,带有 alpha 0 的顶点不可移动,那些带有 alpha 1 的顶点可移动)。
 
顶点法线确定运动方向。
 
然后 Shader 计算到 Camera 的距离,并适当处理表面淡入 / 淡出。
 

ShadowGun中很多Shader就是都是通过网格来模拟光照效果,它们的frag函数一般非常简单而大部分计算都在vert函数中

nfadeout是一个范围在(01)之间的淡化系数。它用于模拟淡入或淡出效果

和它相关的有两个属性:_FadeOutDistNear_FadeOutDistFar

玩家由无限远开始接近这个物体的过程中,一开始是远大于_FadeOutDistFar,那么是看不到这个体积光的

然后逐渐接近_FadeOutDistFar后,开始出现淡入效果

假设小于了_FadeOutDistNear,那么就会开始模拟淡出的效果。

1.3 相关插件

Volumetric_Light_Beam_Kit 是根据Mesh制作体积光的Unity 插件

其原理为生成一个圆锥形Mesh,使用Shader控制颜色和边缘淡入等效果

可调整Radius TopRadius BottomLength

二、光方向挤出实现

这个方案也是一个相对比较省的方案,但是效果的局限性很大,只是某些特殊情况下可以出比较好的效果,主要的思想是阴影的一种实现-体积阴影的扩展

主体思路:

两个Pass,一个Pass渲染本体,另一个Pass渲染体积光

Vert阶段:

确定受光面

进行顶点沿光方向偏移,达到挤出效果

frag阶段

采样贴图,控制衰减

三、径向模糊后处理实现

来源于 GPU Gems3Ch13. Volumetric Light Scattering as a Post-Process

提取高光部分代码

径向模糊部分代码

叠加部分代码

插件介绍

Legacy Image Effect中的 Sun Shaft组件

Lighting Box2 中使用的Sun Shaft组件其实就是Legacy Image Effect中的 Sun Shaft组件

Volumetric Fog Mist插件 中的Light Scattering功能

Volumetric Fog Mist插件的效果会比前面的效果要来的好

四、光线追踪实现

从摄像机发射一条射线到所要渲染的像素

摄像机到所要渲染的像素之间有“介质”(假设是均匀的)

这个“介质”可能就是尘埃之类造成丁达尔效应的胶体

光在介质中传播时,经过的每个路径点都会对最终呈现在屏幕上的像素有所影响


我们从起点开始,沿着射线每次推进一点,采样每个点的亮度,所有经过的采样点上的亮度求和再加上原像素颜色就是最终像素的颜色。

尘埃不可能把所有光都反射到眼睛里的。

光的散射应该是向四面八方的,在一个以尘埃为球心的球体中几乎所有方向都有可能反射到光线,而且每个方向散射出去的光线亮度应该是不一样的,而这些散射出去的光线亮度总合应该和射到尘埃上的那束光线亮度一样,也就是能量守恒。

这种散射可以用一个公式来表示,称为HG公式(Mie-Scattering)

g所表示的就是光的散射系数,g越大,光束越集中,散射越少,g越小,散射越强,θ为光线方向和视线方向的夹角

光在介质内内传播,会被吸收一部分,剩下的部分才能透过介质达到观察者眼中。

Beer-Lambert法则,表现为入射光强和透光强度的比

OutLight = InLight * exp(- c * d)

其中 c为物质密度,d为距离

透光强度随着介质的密度乘光传播的距离的增加呈指数下降。

个问题是这个对象本身的渲染,我们是把这个模型作为一个载体进行渲染的,为了更好的表现效果,不至于被其他东西遮挡住,所以开了ZTest Always,Transparent的Queue是最好。但是我们关闭了ZTest,换句话说,这个物体就不会被遮住了,这也是不现实的。

所以,我们要自己进行一个深度测试,在进行Ray-Marching的过程中,先采样深度值,计算ray的距离,在当前像素对应点的世界坐标距离初始点的距离和当前深度值计算出来的视空间距离比较,用一个更近的距离作为RayMarching的最终距离,这样就可以处理遮挡相关的问题

相关插件介绍

1、VolumetricLights

https://github.com/SlightlyMad/VolumetricLights

Github 1.2K Star

根据《GPU Pro5》体积光技术实现的体积光效果。

2、Hx Volumetric Lighting

https://assetstore.unity.com/packages/vfx/shaders/fullscreen-camera-effects/hx-volumetric-lighting-67665

3、Aura2

https://assetstore.unity.com/packages/tools/particles-effects/aura-2-volumetric-lighting-fog-137148

根据《GPU Pro6》体积光技术实现的体积光效果。

使用了Compute Shader 提高性能

使用3D图预存效果

 

Reference

https://developer.nvidia.com/gpugems/GPUGems3/gpugems3_ch13.html

https://www.shadertoy.com/view/XlBSRz

https://blogs.unity3d.com/2012/03/23/shadowgun-optimizing-for-mobile-sample-level/

https://github.com/usunyu/shadowgun

http://www.javashuo.com/article/p-enlnhotc-dy.html

http://advances.realtimerendering.com/s2014/wronski/bwronski_volumetric_fog_siggraph2014.pdf