unity通过GetVector,GetColor,GetFloat等获取。
SetVector,SetColor,SetFloat等设置。
这里我要修改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。
编辑器脚本如下:
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