Unity一键替换模型中的Shader工具

该博客介绍了一款专为美术设计的工具,能够方便地一键替换Unity模型中的Shader。使用者只需输入旧Shader名称和新Shader名称,即可完成替换操作。文章强调了处理本地Shader与系统Shader的不同方法。

有些需求是一键替换模型中的一些shader,这个工具是给美术使用的,只需要填写要替换的sheder名字,和新的名字即可。

using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using UnityEditor;
using UnityEngine;

//一键替换模型上的Shader
public class AutoReplaceShader
{
    #region 旧版更换shader工具,要在代码中更换地址

    static Shader newShader;   //要替换的很挫的shader   路径: Assets/Resources/Shaders/Normal-Diffuse.shader
    static string newShaderPath = "Assets/Resources/Shaders/Normal-Diffuse.shader";   //要更换的新shader地址(自定义shader)
    static string newShaderName = "Mobile/Diffuse";     //要更换的系统shader路径    

    //白名单
    static string[] writeModelList = { "three004", "parrot_ctrl" };  //如果一个物体,交这个名字,则不更换shader

    //黑名单
    static string[] blackShaderList = { "MXR_multy_Texture_alphaTest2Side", "Diffuse" };                  //如果一个Shader的名字包含这些关键词,则就要被替换掉

    static int changeNum;       //更换shader的数量

    //[MenuItem("Tools/替换Shader")]
    public static void ReplaceShader()
    {
        //1、必须将模型的已生成的预制体Model拖到Actor下面
        GameObject model = GameObject.Find("Actor/Model").gameObject;
        newShader = AssetDatabase.LoadAssetAtPath(newShaderPath, typeof(Shader)) as Shader;
        changeNum = 0;

        MeshRenderer[] meshRs = model.GetComponentsInChildren<MeshRenderer>();
        for (int i = 0; i < meshRs.Length; ++i)
        {
            ChangeShader(meshRs[i].gameObject, meshRs[i].sharedMaterials);
        }

        SkinnedMeshRenderer[] skins = model.GetComponentsInChildren<SkinnedMeshRenderer>();
        for (int i = 0; i < skins.Length; ++i)
        {
            ChangeShader(skins[i].gameObject, skins[i].sharedMaterials);
        }

        AssetDatabase.Refresh();
        Debug.Log("Changer " + changeNum + " shaders over.");
    }


    //更换材质上的shader
    static void ChangeShader(GameObject model, Material[] modelMaters)
    {
        for (int i = 0; i < writeModelList.Length; ++i)
        {
            if (writeModelList[i] == model.name)
            {
                //白名单的物体不修改shader
                return;
            }
        }

        for (int i = 0; i < modelMaters.Length; ++i)
        {
            for (int j = 0; j < blackShaderList.Length; ++j)
            {
                if (!modelMaters[i].shader.name.Contains(blackShaderList[j]))
                {
                    //只有进黑名单的物体才要更换shader
                    continue;
                }

                Debug.Log("Change " + model.name + " shader: " + modelMaters[i].shader.name);
                //modelMaters[i].shader = Shader.Find(newShaderName);
                modelMaters[i].shader = Shader.Find(newShaderName);
                changeNum++;
            }
        }
    }


    #endregion

    #region 新版可编辑修改shader名单

    [MenuItem("Tools/自定义替换Shader")]
    static void ShowReplaceShaderWindow()
    {
        ReplaceShaderWindow shaderWin = EditorWindow.GetWindow<ReplaceShaderWindow>("批量替换Shader界面", true);
        shaderWin.Show();

    }

    #endregion
}



public class ReplaceShaderWindow : BaseEditWindow
{
    //将数据记录下来
    string infoPath = "Assets/Resources/DllTest/ShaderReplaceData.asset";  //名字一定要跟类名保持一致,否则报错
    public static ReplaceShaderWindow _Instance;
    ShaderReplaceData srData;       //自定义名单数据
    UnityEngine.Object SelectObj;   //当前选择要修改shader的模型
    string shaderPath = "Assets/Resources/Shaders/";  //本地shader的存放路径  例如:"Assets/Resources/Shaders/Normal-Diffuse.shader"


    int whiteNum;               //白名单的数量
    int blackNum;               //黑名单的数量
    int changeNum;              //更换shader的数量
    Dictionary<string, string> blackDic = new Dictionary<string, string>();        //将黑名单list转成字典,方便使用    



    protected override void Init()
    {
        _Instance = this;
        GetAssetData();

        whiteNum = srData.whiteList.Count;
        blackNum = srData.blackList.Count;
    }

    protected override void OnGUI()
    {
        base.OnGUI();

        GUILayout.Label("\t\t注意事项\n\n1: 黑名单中必须填写两个shader的名字,前面是要被替换的名字,后面是新的shader名字\n" +
            "2: 要替换的物体和白名单的物体,不要使用同一个material,否则白名单不起作用!\n" +
            "3: 白名单中填写不替换shader的物体名字");
        GUILayout.Space(50);

        if (GUILayout.Button(new GUIContent("替换", "编辑替换shader")))
        {            

            //替换shader todo
            if (CheckBlackListFormat(srData.blackList))
            {
                StartReplaceShader();
                
                SaveAssetData();
                //关闭
                _Instance = null;
                Close();
            }


        }

    }


    protected override void OnShowGUI()
    {

        #region white list

        whiteNum = EditorGUILayout.IntField(new GUIContent("白名单数量: ", "动画切割的数量"), whiteNum);
        //数据比较少,则缩短集合长度
        if (whiteNum < srData.whiteList.Count)
        {
            //数量不一致时,先处理集合            
            for (int i = whiteNum; i < srData.whiteList.Count; ++i)
            {
                srData.whiteList.Remove(srData.whiteList[i]);
            }
        }
        for (int i = 0; i < whiteNum; i++)
        {
            //如果之前数据不够,则先增加集合长度再赋值
            if (i + 1 > srData.whiteList.Count)
            {
                srData.whiteList.Add("");
            }
            srData.whiteList[i] = EditorGUILayout.TextField(new GUIContent(i + ": ", "动画切割的数量"), srData.whiteList[i]);

        }
        EditorGUILayout.Space();
        EditorGUILayout.Space();
        #endregion

        #region black list

        blackNum = EditorGUILayout.IntField(new GUIContent("黑名单数量: ", "动画切割的数量"), blackNum);
        //数据比较少,则缩短集合长度
        if (blackNum < srData.blackList.Count)
        {
            //数量不一致时,先处理集合            
            for (int i = blackNum; i < srData.blackList.Count; ++i)
            {
                srData.blackList.Remove(srData.blackList[i]);
            }
        }
        for (int i = 0; i < blackNum; i++)
        {
            //如果之前数据不够,则先增加集合长度再赋值
            if (i + 1 > srData.blackList.Count)
            {
                srData.blackList.Add("");
            }
            srData.blackList[i] = EditorGUILayout.TextField(new GUIContent(i + ": ", "动画切割的数量"), srData.blackList[i]);

        }

        #endregion

    }


    protected override void OnClose()
    {

    }


    void SaveAssetData()
    {
        ShaderReplaceData data = ScriptableObject.CreateInstance<ShaderReplaceData>();
        data.blackList = srData.blackList;
        data.whiteList = srData.whiteList;
        AssetDatabase.DeleteAsset(infoPath);
        AssetDatabase.CreateAsset(data, infoPath);

        AssetDatabase.SaveAssets();
        AssetDatabase.Refresh();

        Debug.Log(infoPath + " save over");
    }

    void GetAssetData()
    {
        srData = AssetDatabase.LoadAssetAtPath<ShaderReplaceData>(infoPath);
        if (srData == null)
        {
            srData = ScriptableObject.CreateInstance<ShaderReplaceData>();
            Debug.Log("new ShaderReplaceData");
        }
        
        //Debug.Log("get data over");
    }

    //检查黑名单格式是否正确 正确格式"Mobile/Diffuse,UI/Default"
    bool CheckBlackListFormat(List<string> list)
    {
        blackDic.Clear();

        //不能为空
        if (list == null || list.Count <= 0)
        {
            Debug.Log("no shader replace");
            return false;
        }

        //必须包含“,”
        for (int i = 0; i < list.Count; i++)
        {
            if (!list[i].Contains(","))
            {
                Debug.Log(list[i] + ": lack , Please check it");
                return false;
            }
        }

        //必须有原始shader和替换shader
        for (int i = 0; i < list.Count; i++)
        {
            string[] temp = list[i].Split(',');
            if (temp.Length != 2)
            {
                Debug.Log(list[i] + ": not right,you must write 2 shader name");
                return false;
            }

            if (string.IsNullOrEmpty(temp[0]) || string.IsNullOrEmpty(temp[0]))
            {
                Debug.Log(list[i] + ": not right,shader name can not empty");
                return false;
            }

            //将数据放入字典中方便使用
            if (!blackDic.ContainsKey(temp[0]))
            {
                blackDic.Add(temp[0], temp[1]);
            }
            else
            {
                //要替换的shader名字不能重复
                Debug.Log(list[i] + ": name is repeat");
                return false;
            }
        }          
        
        return true;
    }

    //替换shader
    void StartReplaceShader()
    {
        //替换前需要手动选中模型,如果一次性选择很多物体,则默认是第一个
        SelectObj = Selection.activeObject;
        GameObject model = SelectObj as GameObject;
        changeNum = 0;

        if (model == null)
        {
            Debug.Log("请先在Hierarchy面板中选中要替换的模型");
            return;
        }                

        MeshRenderer[] meshRs = model.GetComponentsInChildren<MeshRenderer>();
        for (int i = 0; i < meshRs.Length; ++i)
        {
            ChangeShader(meshRs[i].gameObject, meshRs[i].sharedMaterials);
        }

        SkinnedMeshRenderer[] skins = model.GetComponentsInChildren<SkinnedMeshRenderer>();
        for (int i = 0; i < skins.Length; ++i)
        {
            ChangeShader(skins[i].gameObject, skins[i].sharedMaterials);
        }

        AssetDatabase.Refresh();
        Debug.Log("Changer " + changeNum + " shaders over.");
    }


    //更换材质上的shader,一次只替换一个
    void ChangeShader(GameObject model, Material[] modelMaters)
    {
        for (int i = 0; i < srData.whiteList.Count; ++i)
        {
            if (srData.whiteList[i] == model.name)
            {
                //白名单的物体不修改shader
                return;
            }
        }

        for (int i = 0; i < modelMaters.Length; ++i)
        {           

            if (!blackDic.ContainsKey(modelMaters[i].shader.name))
            {
                //如果不包含在黑名单中,就不用替换这个shader
                continue;
            }


            Debug.Log("Change " + model.name + " shader: " + modelMaters[i].shader.name);
            //因为不知道shader是本地还是系统shader,这里就先从本地查找是否有这个shader
            Shader shader = AssetDatabase.LoadAssetAtPath(shaderPath + blackDic[modelMaters[i].shader.name], typeof(Shader)) as Shader;
            if (shader == null)
            {
                //本地shader找不到,就再次尝试使用系统shader  "UI/Defualt"
                shader = Shader.Find(blackDic[modelMaters[i].shader.name]);
            }

            if (shader != null)
            {
                modelMaters[i].shader = shader;
                changeNum++;

                Debug.Log("success replace shader: " + modelMaters[i].shader.name);
            }
            else
            {
                Debug.LogError(modelMaters[i].shader.name + " not find!!!");
            }


        }
    }


}

public class ShaderReplaceData : ScriptableObject
{    
    public List<string> whiteList = new List<string>();      //白名单,填写Hierarchy面板下物体的名字 "WordTagPlayer"
        
    public List<string> blackList = new List<string>();     //旧名字和新名字,中间用逗号分隔,后面的是新shader名字: "Custom/MXR_multy_Texture_alphaTest2Side,UI/Default,"

}

加载本地shader和加载系统shader方式是不一样的。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值