Unity - Meta Pass 给 Bake GI 的 Emission 自发光等信息无法烘焙到 Lightmap 中的问题


环境

Unity : 2019.4.30f1
Pipeline : Built-In RP


问题

因为场景中需要走完整的 烘焙 方案

然后我们自己写的 VS/FS 着色器

则需要提供 meta pass,而meta pass 也提供了,但结果就是怎么也没有 Emissive 部分烘焙到 Lightmap 中,如下图
在这里插入图片描述

解决后如下:
在这里插入图片描述
在这里插入图片描述


如下,一个最简单的带 meta pass 的着色器

// jave.lin 2021/28

Shader "Test/T3_VerySimpleMetaPass"
{
    
    
    Properties
    {
    
    
        _Color ("Color", Color) = (1, 1, 1, 1)
    }
    SubShader
    {
    
    
        Tags {
    
     "RenderType"="Opaque" }
        LOD 100
        Pass
        {
    
    
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"
            struct appdata
            {
    
    
                float4 vertex : POSITION;
            };
            struct v2f
            {
    
    
                float4 vertex : SV_POSITION;
            };
            fixed4 _Color;
            v2f vert (appdata v)
            {
    
    
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
    
    
                return _Color;
            }
            ENDCG
        }
        // Extracts information for lightmapping, GI (emission, albedo, ...)
        // This pass it not used during regular rendering.
        Pass
        {
    
    
            Name "META"
            Tags {
    
     "LightMode" = "Meta" }
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #pragma target 2.0
            #include "UnityCG.cginc"
            #include "UnityMetaPass.cginc"

            struct v2f
            {
    
    
                float4 pos : SV_POSITION;
                UNITY_VERTEX_OUTPUT_STEREO
            };
            fixed4 _Color;

            v2f vert (appdata_full v)
            {
    
    
                v2f o;
                UNITY_SETUP_INSTANCE_ID(v);
                UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
                o.pos = UnityMetaVertexPosition(v.vertex, v.texcoord1.xy, v.texcoord2.xy, unity_LightmapST, unity_DynamicLightmapST);
                return o;
            }

            half4 frag (v2f i) : SV_Target
            {
    
    
                UnityMetaInput metaIN;
                UNITY_INITIALIZE_OUTPUT(UnityMetaInput, metaIN);

                metaIN.Albedo = 1;
                metaIN.Emission = _Color;
                metaIN.SpecularColor = 1;

                return UnityMetaFragment(metaIN);
            }
            ENDCG
        }
    }
}


解决


解放方案1 - 直接在 Inspector Debug 模型下编辑材质的 Lightmap Flags

在这里插入图片描述

下面是 MaterialGlobalIlluminationFlags 的定义

using System;

namespace UnityEngine
{
    
    
    //
    // 摘要:
    //     How the material interacts with lightmaps and lightprobes.
    [Flags]
    public enum MaterialGlobalIlluminationFlags
    {
    
    
        //
        // 摘要:
        //     The emissive lighting does not affect Global Illumination at all.
        None = 0x0,
        //
        // 摘要:
        //     The emissive lighting will affect realtime Global Illumination. It emits lighting
        //     into realtime lightmaps and realtime lightprobes.
        RealtimeEmissive = 0x1,
        //
        // 摘要:
        //     The emissive lighting affects baked Global Illumination. It emits lighting into
        //     baked lightmaps and baked lightprobes.
        BakedEmissive = 0x2,
        //
        // 摘要:
        //     The emissive lighting is guaranteed to be black. This lets the lightmapping system
        //     know that it doesn't have to extract emissive lighting information from the material
        //     and can simply assume it is completely black.
        EmissiveIsBlack = 0x4,
        //
        // 摘要:
        //     Helper Mask to be used to query the enum only based on whether realtime GI or
        //     baked GI is set, ignoring all other bits.
        AnyEmissive = 0x3
    }
}

可以看到此枚举是 Flags (可按位 “或” 运算):

  • None
    • 0x0,二进制 0000 0000,十进制 0, 不开启任何效果
  • RealtimeEmissive
    • 0x1,二进制 0000 0001,十进制 1,开启 Realtime GI 的 Emissive
  • BakedEmissive
    • 0x2,二进制 0000 0010,十进制 2,开启 Bake GI 的 Emissive
  • EmissiveIsBlack
    • 0x4, 二进制 0000 0100,十进制 4,作用和 None 没什么区别,就不让这个材质不参与 lightmap 中的 emissive 的任何数据提取
  • AnyEmissive
    • 0x3,二进制 0000 0011,十进制 3,就是开启 Realtime GI 和 Bake GI 的 Emissive

显然这种方式是不可取的,因为只能单个在 inspector 面板对需要 Emissive 的材质设置
而且还是直接填写数值的方式,可读性很差


解决方案2 - 脚本批量设置材质的 MaterialGlobalIlluminationFlags

着也是我们推荐的方式

先在 ShaderLab 中给 Pass LightMode = "META" 需要参数 Lightmap Emissive 的添加 "MyEmissionTag"="1" 的 Tag 属性

然后通过脚本过滤所有材质的统一设置

这样就可以在 shaderlab 中设置是否参数 Lightmap Emissive,还是比较方便的

下面是提供参数 Lightmap Emissive 的 ShaderLab 的设置,如 红色框中的内容

在这里插入图片描述

然后使用工具脚本批量设置

// jave.lin 2021/12/28
// 解决自定义的 shader 中,就算带上 meta pass 也无法烘焙 emission 的问题

using UnityEditor;
using UnityEngine;
using UnityEngine.Rendering;

public class SetMatGIFlags
{
    
    
    // jave.lin : 根据 Project 视图中 选中的 Material 来设置为 emissive
    [MenuItem("Tools/SetMatGIFlagsFromSelected")]
    public static void SetMatGIFlagsFromSelected()
    {
    
    
        var objs = Selection.objects;
        var len = objs.Length;
        for (int i = 0; i < len; i++)
        {
    
    
            var mat = objs[i] as Material;
            if (mat == null) continue;
            if (!HasMyEmissionTagPass(mat.shader)) continue;
            // 一个 AnyEmissive = MaterialGlobalIlluminationFlags.RealtimeEmissive| MaterialGlobalIlluminationFlags.BakedEmissive;
            mat.globalIlluminationFlags = MaterialGlobalIlluminationFlags.AnyEmissive;
            Debug.Log($"material : {mat.name}, set to bake the emission flag");
        }
    }

    // jave.lin : 根据 Scene 视图下的 Hierarchy 视图中的所有带有 Renderer 的 mateiral ,如果带有 我们自己定义的 MyEmissionTag 标记的,都标记为 emissive
    [MenuItem("Tools/SetMatGIFlagsFromSceneRenderers")]
    public static void SetMatGIFlagsFromSceneRenderers()
    {
    
    
        var renderers = GameObject.FindObjectsOfType<Renderer>();
        var len = renderers.Length;
        for (int i = 0; i < len; i++)
        {
    
    
            var renderer = renderers[i] as Renderer;
            if (renderer == null) continue;
            var matCount = renderer.sharedMaterials.Length;
            for (int j = 0; j < matCount; j++)
            {
    
    
                var mat = renderer.sharedMaterials[j];
                if (HasMyEmissionTagPass(mat.shader)) continue;
                mat.globalIlluminationFlags = MaterialGlobalIlluminationFlags.AnyEmissive;
                Debug.Log($"go.name : {renderer.gameObject.name}, material: {mat.name}, set to bake the emission flag");
            }
        }
    }
    // jave.lin : 根据 Project 视图资源下所有的材质,如果带有 我们自己定义的 MyEmissionTag 标记的,都标记为 emissive
    [MenuItem("Tools/SetAllMatAssetGIFlag")]
    public static void SetAllMatGIFlag()
    {
    
    
        var matGUIDs = AssetDatabase.FindAssets("t:Material");
        foreach (var guid in matGUIDs)
        {
    
    
            var mat = AssetDatabase.LoadAssetAtPath<Material>(AssetDatabase.GUIDToAssetPath(guid));
            if (mat == null) continue;
            if (!HasMyEmissionTagPass(mat.shader)) continue;
            mat.globalIlluminationFlags = MaterialGlobalIlluminationFlags.AnyEmissive;
            Debug.Log($"material : {mat.name}, set to bake the emission flag");
        }
    }

    // jave.lin : 判断是否带有我们比较的 MyEmissionTag 的 Pass
    private static bool HasMyEmissionTagPass(Shader shader)
    {
    
    
        var passCount = shader.passCount;
        for (int k = 0; k < passCount; k++)
        {
    
    
            var pass = shader.FindPassTagValue(k, new ShaderTagId("MyEmissionTag"));
            if (pass.Equals(ShaderTagId.none)) continue;
            else
            {
    
    
                return true;
            }
        }
        return false;
    }
}


解决方案3 - CustomEditor “MyTestingGIShaderGUI”

给 shader 编写 material editor 的编辑器,然后提供可在 Inspector 面板中对 MaterialGlobalIlluminationFlags 属性的编辑器

这种方式需要额外再 inspector 中设置单个材质的属性

相对 方案2 个人觉得这个方案3没那么方便,特别是想批量设置的时候,就很难维护

下面是提供 ShaderGUI 的编辑器脚本

// jave.lin 2021/12/28
// 测试 自定义带 GI 的 shader 的编辑器
// 参考:https://github.com/Unity-Technologies/UnityCsReference/blob/61f92bd79ae862c4465d35270f9d1d57befd1761/Editor/Mono/Inspector/LegacyIlluminShaderGUI.cs

using UnityEditor;
using UnityEngine;

public class MyTestingGIShaderGUI : ShaderGUI
{
    
    
    public override void OnGUI(MaterialEditor materialEditor, MaterialProperty[] props)
    {
    
    
        base.OnGUI(materialEditor, props);

        //materialEditor.LightmapEmissionProperty(0);

        // We assume that illumin shader always has emission
        foreach (Material material in materialEditor.targets)
        {
    
    
            //material.globalIlluminationFlags &= ~MaterialGlobalIlluminationFlags.EmissiveIsBlack;
            material.globalIlluminationFlags = 
                (MaterialGlobalIlluminationFlags)
                EditorGUILayout.EnumFlagsField(
                    "GI Flag",
                    material.globalIlluminationFlags
                    );
        }

        if (GUILayout.Button("Test"))
        {
    
    
            Debug.Log($"{
      
      nameof(MyTestingGIShaderGUI)} Material Click Test Button.");
        }
    }
}

然后再 ShaderLab 中使用 CustomEditor
在这里插入图片描述


Project

TestingGI_u3d2019_4_30f1


References

猜你喜欢

转载自blog.csdn.net/linjf520/article/details/122191393
今日推荐