Unity体积光

foglight

写在前面

  • 因为我们的毕设决定用HDRP,所以在内置管线做的体积光已经没太大用了
  • 参考Michal巨巨的项目,当初征求大佬许可,大佬很慷慨
    在这里插入图片描述
  • 尝试移植到HDRP,因为CustomPass目前不支持light,导致没有办法采样shadowmap计算衰减,目前打算用自带的顶着,或者放弃降采样功能,直接在场景里做,但我觉得没有必要了。
  • HDRP中LightEvent已经不会触发了,目前只能指望下面这几个节点了
    Points
  • 感觉HDRP还称不上完整

项目分享

内置管线版:
https://github.com/Claymoreno1/volumelight


大体流程

如果抛开光源种类直接但差异,比如我们half resource,Michal大佬的流程是:
深度图降采样(OnPreRender) → DrawMesh(OnLightEvent) → 升采样+与CameraTarget混合(OnRenderImage)
在HDRP中事情变得麻烦:
深度图降采样(After Depth and Normal) → DrawMesh(?) → 升采样+与CameraTarget混合(After Post Process)。
乍一看感觉没啥,实际上你得把这一件事情分成几个CustomPass或者CustomPass+PostEffect。这些也没什么,但是CustomPass 【暂时(用的HDRP 7.17)】不支持Lit,也就是说没有办法直接对ShadowMap采样,这对于FogLight来说是致命的,下图是我按照这个流程在HDRP中做的
在这里插入图片描述
我没有办法做出物体遮挡住光线的效果。我甚至想自己做光源摄像机每帧取景,但这未免也太贵了,重复工作。另一个想法是放弃降采样功能,直接在场景里做,目前为止是可行的。


基于HDRP的具体流程

scaleFactor 0.5 点光源 为例

  1. 创建RT和CB
    对Unity提出表扬,RTHandles确实省了一些事,你不在需要在Update()中检查刷新RT的大小,希望早日去掉实验标志;
  2. 在After Depth and Normal Point 注入Custom Pass
    在这里输出一张降低分辨率的深度图,巨巨进行了一些处理,大体就是对当前前像素点周边采样,当“采样点对齐这个像素”的时候采用周边采样点的最小深度,其他用最大深度。双边模糊类的方法。
  3. 找一个合适的节点DrawMesh
    反正在哪你也拿不到ShadowMap,我是在上一步完成之后直接画的。
    这里不要用Shader/HDRP/CustomPass,那两个函数完全不够用,多Pass也不方便。
    建议现在连连看里把输入的图和需要的坐标连出来,Show Generated Code。
    这部分具体实现在下一部分。
  4. 升采样+叠加到屏幕上
    升采样同样也是类双边模糊的方法。
    叠加有两种做法
    第一种:DrawMesh绘制一个三角形,覆盖屏幕,只写Color不写深度
    第二种:分出去到Before Post Process做Global Volume

体积光的核心计算

  1. 计算RayMarching的起点和终点。
    点光源和聚光灯:计算射线与面的交点;
    直射光:近截面上的点与深度图还原的世界坐标;
  2. 如果我们暂时不加入噪声的话,Michal巨巨用的光照方程就是
    E = 1 4 π 1 g 2 1 + g 2 + 2 g c o s ( θ ) 3 / 2 × ( e d p ) × a t t e n E = \cfrac{1}{4 \pi}{\cfrac{1 - g^2}{1 + g^2 + 2g cos(\theta)^{3/2}}} \times(e^{-dp}) \times atten
    有没有感觉和体积云的像极了!
    我们以 × \times 符号为分隔,这个公式就是Beer’s Law × \times Henyey-Greenstein × \times atten
  3. atten的计算在CustomPass里让人头疼,在内置管线我们可以直接UnityDeferredComputeShadow,即使用CB在Event里。
    我觉得自己再取景一边简直傻逼,暂时用自带的Fog;如果满足不了,只能直接在场景里做了。

写在最后

正所谓技术不够代码来凑,这次就不贴代码增加篇幅了。 要看代码直接在项目里看得了。 其实Michal大佬的体积光,我们在噪声等方面还是有很大提升空间的。 HDRP效果确实很好,但还不够完善。