文章目录
- 环境
- 问题
- 解决
- 解放方案1 - 直接在 Inspector Debug 模型下编辑材质的 Lightmap Flags
- 解决方案2 - 脚本批量设置材质的 MaterialGlobalIlluminationFlags
- 解决方案3 - CustomEditor "MyTestingGIShaderGUI"
- Project
- References
环境
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
References
- 关于自定义shader的自发光烘培
- My Emissive material/shader does not appear in the Lightmap.
- github.com/Unity-Technologies/UnityCsReference/blob/master/Editor/Mono/Inspector/MaterialEditor.cs
- UnityCsReference/Editor/Mono/Inspector/LegacyIlluminShaderGUI.cs