unity 如何修改材质属性和更换shader
作者:Thebluewing 发布时间:2023-02-22 12:42:44
unity通过GetVector,GetColor,GetFloat等获取。
SetVector,SetColor,SetFloat等设置。
这里我要修改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。
编辑器脚本如下:
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


猜你喜欢
- 节能减排,从我做起。一款Android应用如果非常耗电,是一定会被主人嫌弃的。自从Android手机的主人用了你开发的app,一天下来,也没
- 每次看IOS上的应用,应用中状态栏的颜色总能与应用标题栏颜色保持一致,用户体验很不错,对于这种效果,像我这种好奇心强的人就会去看看那安卓是否
- 默认3条以上转为彩信改为5条路径vendor/mediatek/proprietary/packages/apps/Mms/src/com/
- 这篇文章主要介绍了shiro多验证登录代码实例及问题解决,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的
- 前言在讲这两种方式之前,我们先来说明一下什么是java中的jar文件jar (Java Archive File),翻译过来就是java的档
- 下面给大家介绍C#使用ICSharpCode.SharpZipLib.dll进行文件的压缩与解压功能,具体代码如下所示:using Syst
- Struts2Struts2是在WebWork2基础发展而来的。和struts1一样, Struts2也属于MVC框架。不过有一点
- 理解hashCode() 的作用是获取哈希码,也称为散列码;它实际上是返回一个int整数。这个哈希码的作用是确定该对象在哈希表中的索引位置。
- 随机数的定义为:产生的所有数字毫无关系.在实际应用中很多地方会用到随机数,比如需要生成唯一的订单号.在C#中获取随机数有三种方法:一.Ran
- 项目需求为APP的使用单位有很多部门,各个部分的业务也是独立的,所以开发的APP中如果把所有的模块都显示出来然后再做权限分配,会显得屏幕全是
- GB2312是简体中文系统的标准编码 用“区” 跟“位”的概念表示 称之为区位码 区指代大的范围 位相当于偏移量。每个汉字占两个字节高位字节
- 这篇文章主要介绍了Springboot打包部署代码实例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋
- 1. 原因最近学习spring data JPA 时候要用到分页功能,但是发现网上所有教程都是通过new PageRequest()方法解决
- 实例如下所示:public class JsonExtracter { public static void main(String[] a
- 1. 前言随着数据量和调用量的增长,用户对应用的性能要求越来越高。另外,在实际的服务中,还存在着这样的场景:系统在组装数据的时候,对于数据的
- 本文为大家分享了C#实现图书管理系统课程设计,供大家参考,具体内容如下一、设计目的通过模拟图书管理系统,实现以下功能学生账号的注册学生对馆藏
- #region 提示信息#endregion作用:折叠并隐藏代码 ,别且折叠以后能够显示白字“提示信息”如下图就是使用了#region和#e
- @SuppressWarnings 注解@SuppressWarnings: 抑制编译器警告如下,可以看到idea中有警告的颜色标注当我们不
- Spring Security OAuth2主要配置,注意application.yml最后的配置resource filter顺序配置,不
- 已知一副扑克牌有54张,去除大王和小王,剩余52张。在其中随机抽取4张牌,利用加减乘除进行计算得到24. 从A到10,他们的值分别为1到10