Unity的材质有个隐患,当美术同学在切换shader的时候会造成上一个shader里的宏或者属性(如贴图)造成残留,未来在打assetbundle的时候就会造成冗余浪费,Unity这种设计可以更好的解决运行期进行shader的切换,但大部分材质是不需要这种需求的,所以需要一个方法来一键删除残留。
首先使用ShaderUtil.GetShaderGlobalKeywords和GetShaderLocalKeywords来获取当前着色器有哪些宏,方法是内部方法所以需要用到反射,然后就可以取当前材质保存了哪些宏进行比较,最终删除残留的宏Material.shaderKeywords
接着就是残留属性的删除,比如残留贴图。使用shader.GetPropertyName获取当前shader有哪些属性,然后和当前材质中保留的进行比较删除残留的属性。
//获取shader中所有的宏
public static bool GetShaderKeywords(Shader target,out string[] global, out string[] local)
{
try
{
MethodInfo globalKeywords = typeof(ShaderUtil).GetMethod("GetShaderGlobalKeywords", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
global = (string[])globalKeywords.Invoke(null, new object[] { target });
MethodInfo localKeywords = typeof(ShaderUtil).GetMethod("GetShaderLocalKeywords", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
local = (string[])localKeywords.Invoke(null, new object[] { target });
return true;
}
catch
{
global = local = null;
return false;
}
}
[MenuItem("工具/重置着色器")]
static void Test222()
{
//这里替换成自己的材质,也可以深度遍历整个工程中的材质
Material m = AssetDatabase.LoadAssetAtPath<Material>("Assets/aa223.mat");
if (GetShaderKeywords(m.shader,out var global, out var local))
{
HashSet<string> keywords = new HashSet<string>();
foreach (var g in global)
{
keywords.Add(g);
}
foreach (var l in local)
{
keywords.Add(l);
}
//重置keywords
List<string> resetKeywords = new List<string>(m.shaderKeywords);
foreach (var item in m.shaderKeywords)
{
if (!keywords.Contains(item))
resetKeywords.Remove(item);
}
m.shaderKeywords = resetKeywords.ToArray();
}
HashSet<string> property = new HashSet<string>();
int count = m.shader.GetPropertyCount();
for (int i = 0; i < count; i++)
{
property.Add(m.shader.GetPropertyName(i));
}
SerializedObject o = new SerializedObject(m);
SerializedProperty disabledShaderPasses = o.FindProperty("disabledShaderPasses");
SerializedProperty SavedProperties = o.FindProperty("m_SavedProperties");
SerializedProperty TexEnvs = SavedProperties.FindPropertyRelative("m_TexEnvs");
SerializedProperty Floats = SavedProperties.FindPropertyRelative("m_Floats");
SerializedProperty Colors = SavedProperties.FindPropertyRelative("m_Colors");
//对比属性删除残留的属性
for (int i = disabledShaderPasses.arraySize - 1; i >= 0; i--)
{
if (!property.Contains(disabledShaderPasses.GetArrayElementAtIndex(i).displayName))
{
disabledShaderPasses.DeleteArrayElementAtIndex(i);
}
}
for (int i = TexEnvs.arraySize - 1; i >=0; i--)
{
if (!property.Contains(TexEnvs.GetArrayElementAtIndex(i).displayName))
{
TexEnvs.DeleteArrayElementAtIndex(i);
}
}
for (int i = Floats.arraySize - 1; i >= 0; i--)
{
if (!property.Contains(Floats.GetArrayElementAtIndex(i).displayName))
{
Floats.DeleteArrayElementAtIndex(i);
}
}
for (int i = Colors.arraySize - 1; i >= 0; i--)
{
if (!property.Contains(Colors.GetArrayElementAtIndex(i).displayName))
{
Colors.DeleteArrayElementAtIndex(i);
}
}
o.ApplyModifiedProperties();
Debug.Log("Done!");
}
最后目前在项目中已经跑通暂未发现问题,如有疑问欢迎大家补充。
- 本文固定链接: https://www.xuanyusong.com/archives/4796
- 转载请注明: 雨松MOMO于 雨松MOMO程序研究院 发表
捐 赠写博客不易,如果您想请我喝一杯星巴克的话?就进来看吧!


GetShaderGlobalKeywords 也会获取到Local的关键字 这是为啥
GetPropertyCount和GetPropertyName 在unity2018.4版本中需要从shaderUtil中获取。顺便感谢答主。
直接使用旧材质的shader重新创建一个材质,然后把旧材质的数据赋值给新的材质,然后保存新的材质。这样创建出来的材质不会有残留信息,因为它的shader压根就没有修改过。
TA是会修改shader的