UnityShader 基于物理的体积光(丁达尔光线)

体积光在现实世界中也叫丁达尔光
1
体积光在网上也有很多个版本,但是大多数是 以 光照贴图作为 参考最想进行比较。 这样的尽管可以很轻松方便的实现出 体积光,但是他的硬伤确实 精细度太低了,完全无法复原出 体积光该有的 朦胧的感觉。(如下)https://blog.csdn.net/weixin_32938207/article/details/113035729

实现效果如下

黄昏

清晨

光线模拟


其基本原理很简单 ,网上的原理是根据光照深度map于当前的世界坐标点做对比,如果高于 光照深度map,就进行采样计算光照。呢么同理,我们是需要把光照深度map替换成自定义的太阳Depth图。这么一说可能跨度有点大,那么下面我们一步一步仔细给大家讲解实现过程。

丁达尔光线物理原理

丁达尔光是什么
在这里插入图片描述

当一束光线透过胶体,从入射光的垂直方向可以观察到胶体里出现的一条光亮的“通路”,其原理是光被悬浮的胶体粒子所散射。所以胶体粒子散射入射光产生了肉眼可见的光路,被称作丁达尔效应。

丁达尔效应的发现

约翰·丁达尔是英国著名物理学家。他于1869 年发现了丁达尔效应,也是首次解释了为什么天空是蓝色的科学家。
当光线射向分散体系时,只有一部分光能够通过体系,剩余部分则被反射、散射或吸收。体系内物质的化学组成决定了光的吸收量,而体系的分散程度决定了光的散射和反射量。当分散相粒子直径大于入射光的波长时,主要发生光的反射和折射;当入射光照射到直径小于光波长的分散粒子时,则主要发生散射,这时观察到的是光波环绕微粒而向四周放射的光,称为散射光或乳光。丁达尔效应 就是光的散射现象或称乳光现象。
这里 我们可以发现,我们要实现体积光找,我们必须沿着视角方向的光路,进行逐段落采样,获取该光点的光线信息(光的吸收量,散射和反射量等)
在这里插入图片描述

自定义太阳
我们获取深度图的第一个方式 就是通过摄像机获取他的深度贴图,这是我们要注意性能的效果,但是获取深度贴图是一个比较耗时的操作,我们不能直接通过depthTextureMode获取深度图,毕竟代价很大,所以我们需要自己申请两个rendertexture,用来存储从GPU传过来的深度贴图和颜色贴图

   void Start()
    {
    
    
        colorRT = new RenderTexture(Screen.width, Screen.height, 24, RenderTextureFormat.Default);
        depthRT = new RenderTexture(Screen.width, Screen.height, 0, RenderTextureFormat.Depth);
    }
    private void OnPreRender()
    {
    
    
        cam.SetTargetBuffers(colorRT.colorBuffer, depthRT.depthBuffer);
    }

自定义太阳视椎体平面提取

若通过解析方法计算射线与探照灯的投影锥体表面交点,需要首先得到该锥形体的平面方程。Gribb 等人提出了通过投影矩阵快速得到投影平锥体6个平面方程的方法[5],这里简单介绍一下其思路。

设从世界空间到摄像机观察空间的矩阵为 V,投影矩阵 P,我们令 M=PV 为摄像机的 ViewProjection 矩阵。对于空间中任意一点 p = (x, y, z, 1),通过与 ViewProjection 矩阵相乘得到其在裁剪空间下的坐标,并经过齐次除法后得到 NDC 坐标。
如果我们令 ViewProjection 矩阵中4的4行分别为 m_1, m_2, m_3, m_4 ,即

那么 p 在裁剪空间下的坐标为

p 在 NDC 空间下的坐标可以通过下式计算

不妨以左裁剪平面为例,其平面方程在 NDC 空间下为 x_{NDC} = -1 ,即

即是一个平面的标准方程 Ax + By + Cz + D = 0 的向量形式 (A, B, D, D) dot (x, y, z, 1) = 0 ,而平面的法向量即是 n=(A, B, C) ,也可以直接从该式得到,但该式并没有考虑法向量的正负方向。

采样频率

最为一个 很重要的性能消耗指标,采样频率直接影响到了 游戏的帧率。我们在沿视角方向的光路 进行采样的时候,会矩阵转换,深度图提取,光照衰减,以及强度等运算,所以一个很简单的想法就是 减少光路的采样频率,但是随之而来的就是很严重的失真效果
5次采样的结果

呢我们是否可以通过模糊的方式来减少 因为采样过低二造成的梯度显示效果的呢?
Blue Noise
Blue Noise 是一种高频的随机噪声。但 Blue Noise 无法实时生成。开发者 Chistoph Petter 的博客中对 Blue Noise 做了更深入的介绍,在其中也能找到他生成的各种格式和尺寸的 Blue Noise Texture (CC0 授权)http://momentsingraphics.de/BlueNoise.html
此外也有通过去除 White Noise 中的低频成分得到高频的 Blue Noise 的近似实现,此方法也能用于生成各种颜色的噪声(https://blog.demofox.org/2017/1

在这里插入图片描述
在这里插入图片描述

但是很不幸的消息是,做过对应的早点随机采样之后,梯度的效果会消失,但是随之而来的确实黑白不同的颗粒感,如上图所以。

所以我们这时候需要进行后处理大气模糊的效果可以参考https://blog.csdn.net/weixin_51327051/article/details/123031360。最终效果如下图

随机和模糊后

总结
代碼庫
https://github.com/ChenZeMing-xiaoming/Dingder
性能方面的消耗还是有的 ,毕竟需要向真实世界的渲染效果方向努力,所以作为给硬件擦屁股的优化指标,也要把我折磨的快疯掉了(不断的在性能优化 和 渲染效果取最优解)。

猜你喜欢

转载自blog.csdn.net/weixin_43368234/article/details/125729173