Unity引擎材质球残留贴图引用的处理

大家好,我是阿赵。

这次来分享一下Unity引擎材质球残留贴图引用的处理

一、 问题

在使用Unity调整美术效果的时候,我们很经常会有这样的操作,比如:

1、 同一个材质球切换不同的Shader、

比如我现在有2个Shader,其中第一个Shader的参数是这样的:

bash 复制代码
    Properties
    {
        _MainTex ("MainTex", 2D) = "white" {}
		_MaskTex("MaskTex",2D) = "black" {}
		_floatVal("FloatVal",float) = 1
		_rangeVal("RangeVal",Range(0,1)) = 1
		_vecVal ("VectorVal",Vector) = (1,1,1,1)
		_colVal ("ColorVal",Color) = (1,1,1,1)
		
	}

而第二个Shader的参数是这样的:

bash 复制代码
    Properties
    {
        _MainTex ("MainTex", 2D) = "white" {}
		_specTex("SpecTex",2D) = "black" {}
		_floatVal("FloatVal",float) = 1
		_rangeVal("RangeVal",Range(0,1)) = 1
		_vecVal ("VectorVal",Vector) = (1,1,1,1)
		_colVal ("ColorVal",Color) = (1,1,1,1)
		
	}

可以看出,它们两个的参数几乎一样,区别只是在一张贴图的名称不同。

假如先在材质球里选第一个Shader,并且把两个贴图的赋上,现在材质球是这样:

然后在这个材质球里面,把Shader改成第二个Shader,会变成这样:

很明显,第二张贴图由于名字不一样,所以这个Shader上不会显示之前的那张贴图。

这时候疑问来了,换了Shader之后,之前的贴图通道里面的引用,是不是就不用管了呢?

这时候,可以打开Debug来看这个材质球的参数:

可以看到,刚才使用在上一个Shader里面的贴图_MaskTex,其实还是保存在引用里面的。这说明一个问题,假如我们给一个材质球替换Shader,其实上一个Shader使用而当前Shader没有使用的字段参数,都保存在材质球的引用里面。如果我们继续使用这个材质球,那么打包资源的时候很可能就会带着一张我们根本没有用到的贴图。

2、 同一个Shader修改参数的名称

还是拿第一个Shader作为例子:

bash 复制代码
    Properties
    {
        _MainTex ("MainTex", 2D) = "white" {}
		_MaskTex("MaskTex",2D) = "black" {}
		_floatVal("FloatVal",float) = 1
		_rangeVal("RangeVal",Range(0,1)) = 1
		_vecVal ("VectorVal",Vector) = (1,1,1,1)
		_colVal ("ColorVal",Color) = (1,1,1,1)
		
	}

还是把上面的2张贴图都赋上。

bash 复制代码
  这时候,假如我修改一下其中一张贴图的变量名
Properties
    {
        _MainTex ("MainTex", 2D) = "white" {}
		_MaskTex2("MaskTex",2D) = "black" {}
		_floatVal("FloatVal",float) = 1
		_rangeVal("RangeVal",Range(0,1)) = 1
		_vecVal ("VectorVal",Vector) = (1,1,1,1)
		_colVal ("ColorVal",Color) = (1,1,1,1)
		
}

我把_MaskTex改成了_MaskTex2。

这个时候,会发现之前赋予的_MaskTex贴图消失不见了。

这时候问题又来了。是不是修改了Shader里面的变量声明,那么之前声明的变量引用的贴图就消失了呢?

同样的打开Debug模式看:

虽然新增了_MaskTex2的保存项,但实际上原来的_MaskTex贴图还是存在的,引用的贴图也依然存在。所以同样的道理,如果这个时候我们继续使用这个材质球,那么打包的时候,就会包含了一张我们已经不用了的贴图。

通过上面2个例子可以说明,其实材质球上面已经保存过的参数,不论材质球当前的Shader是否有声明变量,都会在Saved Properties里面一直存在着。但如果不用Debug模式去观察,可能很多使用Unity的朋友都不知道这个问题的存在。这可以说是Unity引擎设计上的一个缺陷。一般使用Unity进行美术资源编辑的,都是美工同事们,他们一般都不会去使用Debug模式,而正常模式下的材质球属性显示里面又没有任何的提示,所以虽然包含了多余的美术资源,但他们是很难发现的。

二、 解决问题。

如果我们通过材质球的Reset选项去清空材质球的所有参数

当然是可以把不用的贴图给清理掉。但那样做的话,会顺便把其他我们正常使用的属性也清理掉了。这显然不是我们想要达到的目的。

我们的目的很简单,就是保留现在的Shader用到的参数,去掉已经不需要的参数。Unity似乎没有直接提供这样的手段,起码阿赵我没有找到。不过我们可以通过另外一种迂回一点的手段,去实现这个目的。

思路是这样的:
1、 获得材质球当前使用的Shader的所有属性名称

通过

bash 复制代码
ShaderUtil.GetPropertyCount(shader);

可以获得当前Shader声明的变量个数
2、 使用当前Shader创建一个新的临时材质球

bash 复制代码
Material newMat = new Material(shader);

3、 通过遍历所有属性名称,从旧的材质球上面获取对应的变量参数,然后赋予给新的临时材质球。

由于不同的变量需要用不同的方法获取和赋值,所以需要知道每一个变量的类型,通过

bash 复制代码
ShaderUtil.ShaderPropertyType propType = ShaderUtil.GetPropertyType(shader,i);

可以获得变量类型

类型只有5种,所以根据类型分别去操作就行了。Range和Float其实是一样的,用GetFloat和SetFloat方法获取就行。

4、 通过复制材质球属性,把临时材质球的所有属性赋予给原来的材质球。

通过方法:

bash 复制代码
mat.CopyPropertiesFromMaterial(newMat);

可以把临时材质球的所有属性复制给原来的材质球。
5、 最后保存一下原来的材质球

通过这些手段之后,目的达到了,不使用的变量被清空,需要的变量保留下来。

通过这个思路,可以写一个批处理的工具,批量清理材质球。

三、 源码

根据自己的情况,写一个UnityEditor的工具,然后遍历需要的材质球,传入方法:

bash 复制代码
private void CleanUnusedProp(Material mat)
{
    if(mat == null)
    {
        return;
    }
    //获得当前材质球使用的Shader
    Shader shader = mat.shader;
    //创建一个临时的材质球
    Material newMat = new Material(shader);
    //获得Shader声明的变量的数量
    int propCount = ShaderUtil.GetPropertyCount(shader);

    for(int i = 0;i<propCount;i++)
    {
        //获得变量的类型
        ShaderUtil.ShaderPropertyType propType = ShaderUtil.GetPropertyType(shader,i);
        //获得变量的名称
        string propName = ShaderUtil.GetPropertyName(shader, i);
        //根据变量类型赋值
        switch(propType)
        {
            case ShaderUtil.ShaderPropertyType.Float:
                newMat.SetFloat(propName, mat.GetFloat(propName));
                break;
            case ShaderUtil.ShaderPropertyType.Range:
                newMat.SetFloat(propName, mat.GetFloat(propName));
                break;
            case ShaderUtil.ShaderPropertyType.Vector:
                newMat.SetVector(propName, mat.GetVector(propName));
                break;
            case ShaderUtil.ShaderPropertyType.Color:
                newMat.SetColor(propName, mat.GetColor(propName));
                break;
            case ShaderUtil.ShaderPropertyType.TexEnv:
                newMat.SetTexture(propName, mat.GetTexture(propName));
                break;                
        }
    }
    //复制材质球参数
    mat.CopyPropertiesFromMaterial(newMat);
    //保存材质球
    EditorUtility.SetDirty(mat);
    AssetDatabase.SaveAssets();
    AssetDatabase.Refresh();
}
相关推荐
ttod_qzstudio14 小时前
Unity中使用EzySlice实现模型切割与UV控制完全指南
unity
南無忘码至尊14 小时前
Unity 实现与 Ollama API 交互的实时流式响应处理
unity·游戏引擎·交互
平行云18 小时前
如何实现UE程序大并发多集群的像素流部署
unity·ue5·图形渲染
向宇it2 天前
【unity小技巧】在 Unity 中将 2D 精灵添加到 3D 游戏中,并实现阴影投射效果,实现类《八分旅人》《饥荒》等等的2.5D游戏效果
游戏·3d·unity·编辑器·游戏引擎·材质
向宇it2 天前
Unity Universal Render Pipeline/Lit光照材质介绍
游戏·unity·c#·游戏引擎·材质
__water2 天前
RHA《Unity兼容AndroidStudio打Apk包》
android·unity·jdk·游戏引擎·sdk·打包·androidstudio
两水先木示3 天前
【Unity3D】微信小游戏适配安全区域或胶囊控件(圆圈按钮)水平高度一致方案
unity·微信小游戏·安全区域·ui适配·胶囊控件·safearea
枯萎穿心攻击3 天前
ECS由浅入深第三节:进阶?System 的行为与复杂交互模式
开发语言·unity·c#·游戏引擎
不绝1913 天前
怪物机制分析(有限状态机、编辑器可视化、巡逻机制)
网络·游戏·unity·游戏引擎
unicrom_深圳市由你创科技3 天前
Unity开发如何解决iOS闪退问题
unity·ios·蓝桥杯