15

Unity3D研究院之更换着色器后删除材质残留宏和属性(一百二十五)

 3 years ago
source link: https://www.xuanyusong.com/archives/4796
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

Unity3D研究院之更换着色器后删除材质残留宏和属性(一百二十五)

Unity的材质有个隐患,当美术同学在切换shader的时候会造成上一个shader里的宏或者属性(如贴图)造成残留,未来在打assetbundle的时候就会造成冗余浪费,Unity这种设计可以更好的解决运行期进行shader的切换,但大部分材质是不需要这种需求的,所以需要一个方法来一键删除残留。

Unity3D研究院之更换着色器后删除材质残留宏和属性(一百二十五) - 雨松MOMO程序研究院 - 1

首先使用ShaderUtil.GetShaderGlobalKeywords和GetShaderLocalKeywords来获取当前着色器有哪些宏,方法是内部方法所以需要用到反射,然后就可以取当前材质保存了哪些宏进行比较,最终删除残留的宏Material.shaderKeywords

接着就是残留属性的删除,比如残留贴图。使用shader.GetPropertyName获取当前shader有哪些属性,然后和当前材质中保留的进行比较删除残留的属性。

//获取shader中所有的宏
public static bool GetShaderKeywords(Shader target,out string[] global, out string[] local)
        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!");

最后目前在项目中已经跑通暂未发现问题,如有疑问欢迎大家补充。

作者:雨松MOMO
专注移动互联网,Unity3D游戏开发
捐 赠写博客不易,如果您想请我喝一杯星巴克的话?就进来看吧!

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK