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();
}
相关推荐
_oP_i2 小时前
Unity 中使用 WebGL 构建并运行时使用的图片必须使用web服务器上的
前端·unity·webgl
司军礼6 小时前
Unity自动打包——Shell交互
unity·游戏引擎·交互
无敌最俊朗@11 小时前
unity3d————球形插值知识点
开发语言·学习·unity·c#·游戏引擎
Thomas_YXQ1 天前
Unity3D中管理Shader效果详解
开发语言·游戏·unity·unity3d·游戏开发
孤客网络科技工作室2 天前
Unity3D 开发教程:从入门到精通
unity
刘培玉--大王2 天前
Unity发布微信小程序-实战问题汇总
unity·微信小程序·游戏引擎
Sitarrrr2 天前
【Unity】鼠标点击获取世界坐标位置:物体移动至鼠标点击的位置
unity·游戏引擎
chen_2272 天前
在kanzi 3.9.8里使用API创建自定义材质
c++·材质·kanzi
无敌最俊朗@2 天前
unity3d————游戏对象随机位置移动
学习·游戏·unity·c#·游戏引擎