Unity的材质有个隐患,当美术同学在切换shader的时候会造成上一个shader里的宏或者属性(如贴图)造成残留,未来在打assetbundle的时候就会造成冗余浪费,Unity这种设计可以更好的解决运行期进行shader的切换,但大部分材质是不需要这种需求的,所以需要一个方法来一键删除残留。
首先使用ShaderUtil.GetShaderGlobalKeywords和GetShaderLocalKeywords来获取当前着色器有哪些宏,方法是内部方法所以需要用到反射,然后就可以取当前材质保存了哪些宏进行比较,最终删除残留的宏Material.shaderKeywords
接着就是残留属性的删除,比如残留贴图。使用shader.GetPropertyName获取当前shader有哪些属性,然后和当前材质中保留的进行比较删除残留的属性。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 |
//获取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程序研究院 发表
捐 赠写博客不易,如果您想请我喝一杯星巴克的话?就进来看吧!
GetPropertyCount和GetPropertyName 在unity2018.4版本中需要从shaderUtil中获取。顺便感谢答主。
直接使用旧材质的shader重新创建一个材质,然后把旧材质的数据赋值给新的材质,然后保存新的材质。这样创建出来的材质不会有残留信息,因为它的shader压根就没有修改过。
TA是会修改shader的