Unity自定义SRP(五):烘培光

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Unity自定义SRP(五):烘培光相关的知识,希望对你有一定的参考价值。

参考技术A https://catlikecoding.com/unity/tutorials/custom-srp/baked-light/

​ 到目前为止我们都是在实时渲染光照,除此之外还可以提前计算好光照,然后存储在灯光贴图和探针中,这么做的好处是可以减少实时计算耗费的时间,还可以添加无法实时计算的间接光照,即全局光照,不过烘培光照会提升内存占用。

​ 在Unity的 Window/Rendering/Lighting Settings 中我们可以通过开启 Mixed Lighting 下的 Baked Global Illumination 来设置烘培光,光照模式目前设置为 Baked Indirect :

​ 在 Lightmapping Settings 中可以对灯光贴图进行相关设置。

​ 为使用烘培光,我们需要将 Light 组件下的 Mode 设置为 Mixed 。

​ 在物体的 Mesh Renderer 下的 Lighting 设置中,我们可以开启 Contribute Global Illumination ,并将模式修改为 Lightmaps ,即间接光接触到这些表面时,会将光照信息烘培到灯光贴图中。

​ 在 Lightmapping 下的 Baked Lightmap 中我们可以看到灯光贴图。

​ 使用完全烘培光,我们将模式设置为 Baked ,这样的话就无法使用实时光。

​ 创建 GI.hlsl ,定义一个 GI 结构体,目前先包含漫反射属性,并创建一个 GetGI 方法来获得全局光照信息:

​ 在 GetLighting 中先利用GI初始化颜色:

​ 在片元着色器中应用:

​ 为了获得灯光贴图UV坐标,Unity需要将其送往shader。我们需要指示管线对每个使用灯光贴图的物体进行此操作,可以在 CameraRenderer.DrawVisibleGeometry 中设置 drawSettings 的 perObjectData 属性,这里设置为 PerObjectData.Lightmaps :

​ Unity现在会为开启 LIGHTMAP_ON 关键字的shader变体渲染应用灯光贴图的物体:

​ 灯光贴图的UV坐标是顶点属性的一部分,在 Attributes 中使用 GI_ATTRIBUTE_DATA 宏定义,在 Varyings 中使用 GI_VARYINGS_DATA 定义,在顶点着色器中,使用 TRANSFER_GI_DATA 进行转换:

​ 在片元着色器中获取UV,使用 GI_FRAGMENT_DATA 宏:

​ 实际上上面这些宏没什么意义,我们需要在 GI.hlsl 中定义,如果开启 LIGHTMAP_ON ,我们才为宏赋予意义:

​ 灯光贴图坐标通常由Unity逐网格自动生成,或者是导入网格数据的一部分。网格会进行纹理展开,这样就可以映射到纹理坐标,这个展开操作会在灯光贴图中逐物体的进行缩放和平移,所以每个实例会有自己的空间。

​ 灯光贴图的UV变换是 UnityPerDraw 缓冲的一部分,我们添加下面两个变量:

​ 然后调整 TRANSFER_GI_DATA 宏,应用变换操作:

​ 我们需要运用到 Core RP 中的 EntityLighting.hlsl 。定义纹理和采样器:

​ 定义采样方法 SampleLightMap ,调用 SampleSingleLightmap 方法:

​ SampleSingleLightmap 方法除UV外要求多个参数,首先需要传入纹理和采样器状态,我们可以使用 TEXTURE2D_PARAM 宏组合:

​ 在UV后是缩放和平移变换,由于已经做过,这里设为单位变换即可:

​ 然后是指示灯光贴图是否被压缩,如果定义了 UNITY_LIGHTMAP_FULL_HDR 的话就要设为false,最后的参数包含解码指令:

​ SampleSingleLightmap :

​ 动态物体并不会影响烘焙的全局光照,但可以通过光照探针影响。光照探针是场景中的一个点,通过一个三次多项式,特别是L2球面谐波来近似烘培所有的入射光。光照探针可以拜访在场景周围,Unity会逐物体对这些探针插值来达到近似的光照结果。

​ 光照探针通过创建光照探针组来添加,通过 GameObjectLight/Light Probe 。默认是包含个探针的立方体。

​ 场景中可以有多个探针组,Unity将所有的探针组合在一起,然后创建一个四面体网体来连接所有探针。每个动态物体处在一个四面体网格中。四面体的四个顶点的探针会被插值,获得最终光照后应用到物体上。如果一个物体位于探针覆盖区域范围外,会使用最邻近的三角形,这样光照会看起来很奇怪。

​ 默认情况下,当一个动态物体被选中后,会显示哪些影响它的探针,以及一个插值后的位置。

​ 如何拜访光照探针取决于场景。首先,只有动态物体才需要探针;其次,光照变化的地方也可以摆放探针,每个探针是插值的终点,因此将它们围绕着光照过渡的地方拜访;第三,不要再烘培几何体内摆放探针,这样会变黑;最后,插值是会遍历所有物体的,因此,当光照在一个物体的对立面不同时,探针要尽可能的靠近两面。

​ 同理,探针插值数据需要送往GPU,这也是一个逐物体数据:

​ shader中我们需要的数据如下:

​ 在 GI 中采样光照探针,定义 SampleLightProbe 方法:

​ SampleSH9 需要探针数据和法线方向:

​ 球面谐波相关介绍略。

​ 在 GetGI 中应用:

​ 光照探针对小的动态物体比较好使,但因为光照时基于单个点的,因此对于更大的物体就不好使了。

​ 对于这些大物体,我们可以添加 LightProbeProxyVolume 组件,简称LPPV,这样就可以匹配大物体。

​ 同理,配置逐物体数据属性:

​ shader中,我们需要下面这些数据:

​ 体数据存储在3D纹理中, unity_ProbeVolumeSH ,在 GI 中定义:

​ 是使用LPPV还是插值的光照探针,取决于 unity_ProbeVolumeParams 的第一个组件。采样探针代理体我们使用 SampleProbeVolumeSH4 方法,参数依次是纹理和采样器,世界空间位置,世界空间法线, unityProbeVolumeWorldToObject 矩阵, unity_ProbeVolumeParams 的YZ组件,然后是SizeInv和Min的xyz组件:

SampleProbeVolumeSH4 :

​ 因为间接漫反射光与表面作用后应该会会被表面的漫反射率影响,对此,Unity使用一个特殊的meta pass在烘培时来决定反射光 。

​ 新建一个 LitInput.hlsl 文件,将变量放入,避免重复声明。同时构建一些获取属性的方法,隐藏Unity自带的宏:

​ 在shader的开头,我们包含这些公用文件:

​ Meta Pass的Light Mode我们设置为 Meta :

​ 接着构建最基本的顶点和片元着色器。 ZERO_INITIALIZE 可用于初始化结构体变量:

​ 定义光照贴图UV,将其作为局部空间坐标的XY组件:

​ OpenGL需要精确配置局部坐标的Z组件:

​ meta pass用于生成不同的数据,通过 unity_MetaFragmentControl 通信控制:

​ x组件控制漫反射:

​ Unity的meta pass会将meta值加上通过粗糙度控制的镜面反射率的一半,用于提亮,让高光反射但粗糙的物体也可以传递一些间接光:

​ 之后,结果通过一个幂运算提升:

​ 之后,在 GetLighting 中应用表面漫反射率:

​ 在shader中添加新的属性:

​ 在 LitInput 中声明对应的属性和方法:

​ 在片元着色器末尾调用:

​ 在meta pass中,使用 unity_MetaFragmentControl 的y组件控制发光:

​ 在 CustomShaderGUI 中,我们需要手动开启选项:

​ 这样就会出现选项:

​ 使用 Baked 表明烘培发光。

​ 但Unity尽量在烘培时避免额外的发光pass,如果材质的发光被设为0就会被忽略。为此,在发光模式变化时,我们可以使用 MaterialGlobalIlluminationFlags.EmissiveIsBlack 来重写 globalIlluminationFlags ,这样,只有在要烘培时才会开启:

​ Unity的Lightmapper对透明使用硬编码方法,即硬性规定使用 _MainTex 、 _Color 、 _Cutoff ,目前我们只支持了 _Cutoff ,为此,需要额外定义两个属性,并打上 [HideInInspector] 标签:

​ 我们需要确保 _MainTex 与我们的 _BaseMap 对应,我们定义一个 CopyLightMappingProperties 方法来进行属性复制:

​ 不过,这也就意味着烘培透明物体只能依赖于单张纹理、颜色和裁剪属性,同时lightmapper也只考虑材质的属性,逐实例属性不考虑。

以上是关于Unity自定义SRP(五):烘培光的主要内容,如果未能解决你的问题,请参考以下文章

Unity自定义SRP(十三):颜色分级

Unity自定义SRP(十五):SSAO

Unity自定义SRP(十四):抗锯齿和缩放渲染

Unity Lighting 面板的参数设置用途详细总结

可编程脚本渲染管线SRP

Unity开坑之模型定制自定义插件UMA