参考GitHub - SlightlyMad/VolumetricLights: Volumetric Lights for Unity这里实现的体积光会包括几个方面的影响。
制作的核心:
核心是灯光类型(平行光,点光源,聚光灯)。这些灯光类型会受到米尔散射通透度影响,高度指数雾,级联阴影,噪音影响以及降噪。
原理:
无论哪个光照,他都会按照步进的次数跟摄像机的四个视角顶点的距离做比较,然后再跟深度以及地面比较,看究竟跟哪个最近则选择谁是最终碰撞点,然后选取颜色。
平行光:
首先_FrustumCorners存储的是视野的四个角的位置世界坐标。
顶点着色:
在顶点着色中通过i.uv.x + i.uv.y*2来获取_FrustumCorners的索引,看究竟这个顶点跟在屏幕空间究竟跟哪个角最接近,则方向就以这个角为终点的xy值。
片元着色:
在片元着色器中摄像机位置为起点,顶点着色得到的的摄像机的角再乘深度值得到世界坐标值。然后除总长度得到他的步长单位方向,然后给到RayMarch去步进
RayMarch
再RayMarch中,通过步进总长度除步进次数得到每次步进的长度。
然后首先获取灯光的阴影GetLightAttenuation,他这里考虑了级联阴影,通过GetCascadeWeights_SplitSpheres来获取位置与级联阴影的的距离,因为最多四个级联阴影所以刚好记录在一个float4中,然后记录到权重中,权重的值要拿级联的前后级相减来得到差异。
拿到的权重带入到GetCascadeShadowCoord中得到采样的级联的位置。主要是采样四个级联的阴影然后根据权重混合起来
如果在级联范围内则采样这个权重级联的值,不然直接算作1.
除了拿到阴影,还要拿密度GetDensity,他的密度跟x轴和z轴有关,z轴越远密度越高。
密度这里如果开启了高度相关的雾效则会执行ApplyHeightFog,主要是跟高度成指数关系变化。
然后拿到密度后根据步进步长计算通透和衰减。衰减成指数衰减。
步进完成得到颜色后会加上mie散射的值,mie散射强度与频率的二次方成正比,并且散射在光线向前方向比向后方向更强,方向性比较明显。在从c#中的MieG即使米尔散射的强度。
shader中:
点光源:
片元着色:
点光源要关注到到要步进的最长长度,这里要注意的是首先会先拿到raylength,他是像素位置的世界坐标到相机的位置的距离“长度A”,因为这个距离内可能会有被挡着的对象,所以还需要拿到相机的朝向做步进的投影,让深度除投影的值来得到深度上最大的步长“长度B”。然后长度A和长度B的最小值就是步进的最大值。
RayMarch
然后还是执行RayMarch来步进,这里的米尔散射会在步进的时候计算,他跟平行光有差异,平行光只需要步进完一次性计算,但是点光会有衰减,所以要步进过程中计算。
后面的计算跟平行光一样。
聚光灯:
聚光灯需要关注三个的最小值才为相交的物体
第一个是关注平面RayPlaneIntersect,这里个到两个参数可以确定平面信息,一个是平面的法线,一个是平面方程ax + by + cz + d = 0的d。然后按投影的方式得到t,这个t就是平面和点的距离。
第二个关注的是聚光灯的圆锥范围内的相交RayConeIntersect。
第三个是深度
三个选最小的
然后进行步进RayMarch,步进方式跟点光源一样。