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

作者:Thebluewing 时间:2023-02-22 12:42:44 

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

SetVector,SetColor,SetFloat等设置。

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

这里我要修改Transparency_Value的值

使用setfloat修改值


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

这是shader里面的一句


_TransVal("Transparency_Value", Range(0,1)) = 0.5

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

编辑器脚本如下:


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等类型),修改后应将该资源标记为已更改:


EditorUtility.SetDirty(Object target)

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

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

来源:https://blog.csdn.net/Thebluewing/article/details/72820162

标签:unity,材质属性,shader
0
投稿

猜你喜欢

  • java实现静默安装apk

    2023-08-31 08:11:34
  • Gson之toJson和fromJson方法的具体使用

    2021-07-20 16:28:47
  • Android Compose 属性动画使用探索详解

    2022-08-07 11:06:57
  • 解决Spring或SpringBoot开启事务以后无法返回自增主键的问题

    2023-09-12 18:51:45
  • 10分钟带你理解Java中的弱引用

    2023-02-09 10:35:55
  • java 画pdf用itext调整表格宽度、自定义各个列宽的方法

    2021-07-12 04:16:10
  • 如何设计一个安全的API接口详解

    2023-03-06 14:57:03
  • C#中按引用传递与按值传递的区别,以及ref与out关键字的用法详解

    2023-01-26 00:27:17
  • Android实现流光和光影移动效果代码

    2023-09-24 23:57:55
  • struts2拦截器_动力节点Java学院整理

    2023-06-11 10:11:36
  • Android开发之MediaPlayer基本使用方法详解

    2022-09-05 12:21:38
  • Java异步处理机制实例详解

    2022-05-12 01:39:37
  • Eclipse配置maven环境的图文教程

    2021-11-15 19:06:02
  • SpringMVC后端返回数据到前端代码示例

    2023-06-20 13:12:47
  • Java中break、continue、return在for循环中的使用

    2023-03-21 23:36:05
  • Java中对象的序列化方式克隆详解

    2021-09-15 20:02:01
  • java时区转换的理解及示例详解

    2022-01-19 08:35:20
  • C# cmd中修改显示(显示进度变化效果)的方法

    2023-11-02 13:55:34
  • C# 构造函数如何调用虚方法

    2023-05-12 00:08:57
  • android打开本地图像的方法

    2022-10-26 08:01:42
  • asp之家 软件编程 m.aspxhome.com