服务器之家:专注于VPS、云服务器配置技术及软件下载分享
分类导航

PHP教程|ASP.NET教程|Java教程|ASP教程|编程技术|正则表达式|C/C++|IOS|C#|Swift|Android|VB|R语言|JavaScript|易语言|vb.net|

服务器之家 - 编程语言 - C# - unity 如何修改材质属性和更换shader

unity 如何修改材质属性和更换shader

2022-11-14 12:13Thebluewing C#

这篇文章主要介绍了unity 修改材质属性和更换shader的操作方式,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧

unity通过GetVector,GetColor,GetFloat等获取。

SetVector,SetColor,SetFloat等设置。

unity 如何修改材质属性和更换shader

这里我要修改Transparency_Value的值

使用setfloat修改值

?
1
code  renderer.material.SetFloat("_TransVal", TranValue);

这是shader里面的一句

?
1
_TransVal("Transparency_Value", Range(0,1)) = 0.5
?
1
code renderer.material.shader = Shader.Find("Custom/SimpleAlpha");

代码控制切换shader。

补充:Unity 利用编辑器扩展批量修改物体材质的Shader并启用GPU Instancing

为什么会有这个需求

我的某个游戏运行之后,看了下draw call,发现上千个draw call了,非常大的数值,不过我在手机上测试了一下,竟然没有明显的卡顿,哈哈哈,很强,不过还是要优化一下的,所以先想办法降低draw call了,我看了一个,是游戏的地图产生了大量的dc,我这个游戏是由四个地图组成的,每个地图都由几百个小物体组成,所以四个地图应该是由两千多个物体组成的,刚开始我想着要不合并模型的网格试试吧,然后发现出问题了,一些显示一些隐藏了,可能是太多物体了,合并容易出问题吧,所以我就打算启用Shader中的Enable GPU Instancing,启用后,会自动进行静态批处理,所以dc就会大幅度的减少。

而且我发现我的游戏物体的材质Shader还没有Enable GPU Instancing,想着自己写个有Enable GPU Instancing的Shader吧,但是我又看了一下,Unity中的Mobile/Diffuse的Shader就有这个选项,然后就用这个Shader了吧,那么问题又来了,两千多个物体,难道要我自己一个一个的改Shader并且启用GPU Instancing吗?当然这样也行,前提是你很闲,无聊到没事做的时候可以这样做。

所以我的办法是自己写个编辑器脚本来批量修改Shader并启用GPU Instancing。

unity 如何修改材质属性和更换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
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
using System.Collections.Generic;
using UnityEngine.SceneManagement;
using UnityEditor;
using UnityEngine;
public class ReplaceShaderByFileDir : EditorWindow
{
    Shader shader;
    Shader originShader;
    bool isShowReplaceGo = false//是否显示被替换的物体
    string tipMsg = null;
    MessageType tipMsgType = MessageType.Info;
    List<GameObject> replaceGoList = new List<GameObject>();
    int matCount = 0;   //材质的数量
    Vector2 scrollPos = Vector2.zero;
    [MenuItem("Editor/替换场景中的shader")]
    public static void OpenWindow()
    {
        //创建窗口
        ReplaceShaderByFileDir window = GetWindow<ReplaceShaderByFileDir>(false, "替换场景中的shader");
        window.Show();
    }
    void OnGUI()
    {
        GUILayout.Label("原shader:");
        originShader = (Shader)EditorGUILayout.ObjectField(originShader, typeof(Shader), true);
        //ObjectField(string label, Object obj, Type objType, bool allowSceneObjects, GUILayoutOption[] paramsOptions)
        //label字段前面的可选标签   obj字段显示的物体   objType物体的类型    allowSceneObjects允许指定场景物体..
        //返回:Object,用户设置的物体
        GUILayout.Label("替换shader :");
        shader = (Shader)EditorGUILayout.ObjectField(shader, typeof(Shader), true);
        GUILayout.Space(8);
        //开始一个水平组,所有被渲染的控件,在这个组里一个接着一个被水平放置。该组必须调用EndHorizontal关闭。
        GUILayout.BeginHorizontal();
        if (GUILayout.Button("批量替换", GUILayout.Height(30)))
        {
            Replace();
        }
        if (GUILayout.Button("重置", GUILayout.Height(30)))
        {
            Reset();
        }
        //关闭水平组
        GUILayout.EndHorizontal();
        //提示信息
        if (!string.IsNullOrEmpty(tipMsg))
        {
            //创建一个帮助框,第一个参数是显示的文本,第二个参数是帮助框的提示图标类型
            EditorGUILayout.HelpBox(tipMsg, tipMsgType);
        }
        //创建勾选框
        isShowReplaceGo = GUILayout.Toggle(isShowReplaceGo, "显示被替换的GameObject");
        if (isShowReplaceGo)
        {
            if (replaceGoList.Count > 0)
            {
                //开始滚动视图,scrollPos用于显示的滚动位置
                scrollPos = GUILayout.BeginScrollView(scrollPos, GUILayout.Width(Screen.width), GUILayout.Height(Screen.height - 200));
                foreach (var go in replaceGoList)
                {
                    EditorGUILayout.ObjectField(go, typeof(GameObject), true);
                }
                //结束滚动视图
                GUILayout.EndScrollView();
            }
            else
            {
                EditorGUILayout.LabelField("替换个数为0");
            }
        }
    }
    /// <summary>
    /// 替换Shader
    /// </summary>
    void Replace()
    {
        replaceGoList.Clear();
        if (shader == null)
        {
            tipMsg = "shader为空!";
            tipMsgType = MessageType.Error;
            return;
        }
        if (originShader == null)
        {
            tipMsg = "指定的shader为空!";
            tipMsgType = MessageType.Error;
            return;
        }
        else if (originShader.Equals(shader))
        {
            tipMsg = "替换的shader和指定的shader相同!";
            tipMsgType = MessageType.Error;
            return;
        }
        Dictionary<GameObject, Material[]> matDict = GetAllScenceMaterial();
        List<Material> replaceMatList = new List<Material>();
        foreach (var item in matDict)
        {
            GameObject tempGo = item.Key;
            Material[] mats = item.Value;
            int length = mats.Length;
            for (int i = 0; i < length; i++)
            {
                var mat = mats[i];
                if (mat != null && mat.shader.Equals(originShader))
                {
                    if (!mat.shader.Equals(shader))
                    {
                        replaceGoList.Add(tempGo);
                        if (!replaceMatList.Contains(mat))
                            replaceMatList.Add(mat);
                    }
                }
            }
        }
        //替换Material的数量
        int replaceMatCount = replaceMatList.Count;
        for (int i = 0; i < replaceMatCount; i++)
        {
            UpdateProgress(i, replaceMatCount, "替换中...");
            //替换Shader
            replaceMatList[i].shader = shader;
            //启用GPU Instancing
            replaceMatList[i].enableInstancing = true;
            //设置脏标志,标记目标物体已改变,当资源已改变并需要保存到磁盘,Unity内部使用dirty标识来查找
            EditorUtility.SetDirty(replaceMatList[i]);
        }
        // 刷新编辑器,使刚创建的资源立刻被导入,才能接下来立刻使用上该资源
        AssetDatabase.Refresh();
        // 一般所有资源修改完后调用,调用后Unity会重新导入修改过后的资源
        AssetDatabase.SaveAssets();
        tipMsg = "替换成功!替换了" + replaceMatCount + "个Material," + replaceGoList.Count + "个GameObject";
        tipMsgType = MessageType.Info;
        //关闭进度条
        EditorUtility.ClearProgressBar();
    }
    /// <summary>
    /// 替换shader的可视化进程
    /// </summary>
    void UpdateProgress(int progress, int progressMax, string info)
    {
        string title = "Processing...[" + progress + " / " + progressMax + "]";
        float value = (float)progress / progressMax;
        //显示进度条
        EditorUtility.DisplayProgressBar(title, info, value);
    }
    /// <summary>
    /// 重置
    /// </summary>
    void Reset()
    {
        tipMsg = null;
        shader = null;
        originShader = null;
        matCount = 0;
        replaceGoList.Clear();
        isShowReplaceGo = false;
    }
    /// <summary>
    /// 获取所有场景中的Material
    /// </summary>
    /// <returns></returns>
    Dictionary<GameObject, Material[]> GetAllScenceMaterial()
    {
        Dictionary<GameObject, Material[]> dict = new Dictionary<GameObject, Material[]>();
        List<GameObject> gos = GetAllSceneGameObject();
        foreach (var go in gos)
        {
            Renderer render = go.GetComponent<Renderer>();
            if (render != null)
            {
                Material[] mats = render.sharedMaterials;
                if (mats != null && mats.Length > 0 && !dict.ContainsKey(go))
                {
                    dict.Add(go, mats);
                    matCount += mats.Length;
                }
            }
        }
        return dict;
    }
    /// <summary>
    /// 获取所有场景中的物体
    /// </summary>
    /// <returns></returns>
    List<GameObject> GetAllSceneGameObject()
    {
        List<GameObject> list = new List<GameObject>();
        //获取当前活动的场景
        Scene scene = SceneManager.GetActiveScene();
        //获取场景中所有根游戏对象
        GameObject[] rootGos = scene.GetRootGameObjects();
        foreach (var go in rootGos)
        {
            Transform[] childs = go.transform.GetComponentsInChildren<Transform>(true);
            foreach (var child in childs)
            {
                list.Add(child.gameObject);
            }
        }
        return list;
    }
}

在编写编辑器时,如果需要修改Unity序列化资源(如Prefab,美术资源,ScriptableObject等类型),修改后应将该资源标记为已更改:

?
1
EditorUtility.SetDirty(Object target)

但标记为已更改的资源Unity不会立即保存到磁盘,这时需要调用 AssetDataBase.SaveAssets(),一般所有资源修改完后调用,调用后Unity会重新导入修改过后的资源(数量大费时间)。

以上为个人经验,希望能给大家一个参考,也希望大家多多支持服务器之家。如有错误或未考虑完全的地方,望不吝赐教。

原文链接:https://blog.csdn.net/Thebluewing/article/details/72820162

延伸 · 阅读

精彩推荐
  • C#c# wpf如何附加依赖项属性

    c# wpf如何附加依赖项属性

    这篇文章主要介绍了c# wpf如何附加依赖项属性,帮助大家更好的理解和学习使用c#,感兴趣的朋友可以了解下...

    杜文龙9772022-11-09
  • C#在WinForm中发送HTTP请求的实现方法

    在WinForm中发送HTTP请求的实现方法

    下面小编就为大家带来一篇在WinForm中发送HTTP请求的实现方法。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧...

    C#教程网8682021-11-24
  • C#关于C#执行顺序带来的一些潜在问题

    关于C#执行顺序带来的一些潜在问题

    这篇文章主要给大家介绍了关于C#执行顺序带来的一些潜在问题,文中通过示例代码介绍的非常详细,对大家学习或者使用C#具有一定的参考学习价值,需要...

    UWP爱好者10352022-10-07
  • C#解析C#多线程编程中异步多线程的实现及线程池的使用

    解析C#多线程编程中异步多线程的实现及线程池的使用

    这篇文章主要介绍了C#多线程编程中异步多线程的实现及线程池的使用,同时对多线程的一般概念及C#中的线程同步并发编程作了讲解,需要的朋友可以参考下...

    懒得安分4902021-11-15
  • C#C#中重载与重写区别分析

    C#中重载与重写区别分析

    这篇文章主要为大家详细介绍了C#中重载与重写的区别,感兴趣的小伙伴们可以参考一下...

    C#教程网9622021-11-12
  • C#C# 忽略大小写进行字符串比较

    C# 忽略大小写进行字符串比较

    这篇文章主要介绍了C# 字符串比较忽略大小写的方法,帮助大家更好的理解和使用c#,感兴趣的朋友可以了解下...

    许鸿飞10092022-11-01
  • C#C#中事件的定义和使用

    C#中事件的定义和使用

    在使用事件时,通常要定义两个方法,一个是和事件定义的委托签名一致的方法。下面让我们看看使用事件的具体步骤。...

    张逸8322021-11-24
  • C#使用C#写了一个可以推算火车票身份证号码的小程序

    使用C#写了一个可以推算火车票身份证号码的小程序

    这篇文章主要介绍了使用C#写了一个可以推算火车票身份证号码的小程序 的相关资料,需要的朋友可以参考下...

    葛云飞6042021-11-11