您的位置:首页 > 移动开发 > Unity3D

Mesh中 material 和 sharedMaterial 的区别及内部实现的推断

2017-05-10 23:02 260 查看

material 和 sharedMaterial 的区别

创建一个Material, 颜色为红色,

创建两个Quad,挂上刚刚创建的材质。 效果如下图:



将第一个Quad挂载如下脚本, 运行:

render = GetComponent<Renderer>();
render.material.color = Color.white;


效果如下图:



修改脚本内容如下, 运行:

render = GetComponent<Renderer>();
render.sharedMaterial.color = Color.white;


Quad的颜色变了

效果如下图:



如何来解释上面的现象呢, 根据官方文档

Renderer.sharedMaterial

sharedMaterial 是共用的 Material,称为共享材质。修改共享材质会改变所用使用该材质的物体,并且编辑器中的材质设置也会改变。

Renderer.material

material 是独立的 Material,返回分配给渲染器的第一个材质。修改材质仅会改变该物体的材质。如果该材质被其他的渲染器使用,将克隆该材质并用于当前的渲染器。

推测 sharedMaterial 和 material 的实现

下面我们通过一些实现猜测 material 是如何实现的:

我们从 UnityEngine.Renderer 中可得知 sharedMaterial 和 material 是属性(property),当给属性赋值时会隐式的调用 set 方法,当获取属性值得时候会隐式的调用 get 方法。假设 sharedMaterial 的值变量假设为 _sharedMaterial, material 的值变量假设为 _material。

Material _material;
Material _sharedMaterial;


第一个测试:

Material origin_sharedMat = render.sharedMaterial;
Debug.Log(origin_sharedMat == render.sharedMaterial);
// True


调用了 sharedMaterial 的 get方法。 可以推断出 sharedMaterial 的 get:

return _sharedMaterial;


第二个测试:

Material new_Mat = new Material(Shader.Find("Standard"));
render.sharedMaterial = new_Mat;
Debug.Log(new_Mat == render.sharedMaterial);
// True


调用了 sharedMaterial 的 set方法。 可以推断出 sharedMaterial 的 set:

_sharedMaterial = value;


第三个测试:

Material origin_sharedMat = render.sharedMaterial;
Material origin_Mat = render.material;
Debug.Log(origin_sharedMat == render.sharedMaterial);
Debug.Log(origin_Mat == render.sharedMaterial);
Debug.Log(origin_Mat == render.material);
// Flase True True


在隐式调用 material 的 get 后, sharedMaterial 被修改。且 _material 和 _sharedMaterial 相等。

推断 material 的 get:

if (_sharedMaterial ~= _material) {
_material = new Material (_sharedMaterial);//这一步由第四个测试共同推出
_sharedMaterial = _material;

}
return _material;


第四个测试:

Material new_Mat = new Material(Shader.Find("Standard"));
render.sharedMaterial = new_Mat;
Debug.Log(new_Mat == render.sharedMaterial);
Material origin_Mat = render.material;
Debug.Log(new_Mat == origin_Mat);
Debug.Log(origin_Mat == render.sharedMaterial);
Debug.Log(origin_Mat == render.material);
// True Flase True True


很明显 _material的初始化并未通过 material 属性,因为未调用 material 属性的 get 方法前,_sharedMaterial 和 _material并不相等。

推断 sharedMaterial 的 set:

_sharedMaterial = value;


第五个测试:

Material new_Mat = new Material(Shader.Find("Standard"));
render.material = new_Mat;
Debug.Log(new_Mat.name);
Debug.Log(render.sharedMaterial.name);
Debug.Log(render.material.name);
// Standard Standard Standard(Instance)


推测 material 的 set:

_sharedMaterial = value;


综上推断:

public Material material {
get {
if (_sharedMaterial ~= _material) {
_material = new Material (_sharedMaterial);
_sharedMaterial = _material;

}

4000
return _material;
}
set {
_sharedMaterial = value;
}
}

public Material sharedMaterial {
get {
return _sharedMaterial;
}
set {
_sharedMaterial = value;
}
}


使用 material 时的内存泄漏问题

每一次引用 Renderer.material 的时候,都会生成一个新的 material 到内存中去,销毁物体的时候需要我们手动去销毁该material,否则会一直存在内存中。

官方文档说:

This function automatically instantiates the materials and makes them unique to this renderer. It is your responsibility to destroy the materials when the game object is being destroyed. Resources.UnloadUnusedAssets also destroys the materials but it is usually only called when loading a new level.

此方法自动实例化该材质并且使其成为该渲染器独有的材质。当该游戏物体被删除时,你应该手动删除该材质。当替换场景调用 Resources.UnloadUnusedAssets 也可以删除该材质。

网上的解决方案如下:

http://www.xuanyusong.com/archives/2530

编辑器下使用 material, 其他平台使用 sharedMaterial

public static Material GetMaterial(Renderer render)
{
#if UNITY_EDITOR
return render.material;
#else
return render.sharedMaterial;
#endif
}


http://www.jianshu.com/p/ababf547d992

如果是主角这一类gameobject身上需要修改材质的属性或者shader属性比较多的时候,可以第一次使用material,这样可以动态的生成一个material实例,然后再使用sharedmaterial,动态的修改这个新生成的material,而且不会创建新的material。

https://blog.uwa4d.com/archives/optimzation_memory_2.html

一般情况下,资源属性的改变情况都是固定的,并非随机出现。比如,假设GameObject受到攻击时,其Material属性改变随攻击类型的不同而有三种不同的参数设置。那么,对于这种需求,我们建议你直接制作三种不同的Material,在Runtime情况下通过代码直接替换对应GameObject的Material,而非改变其Material的属性。这样,你会发现,成百上千的instance Material在内存中消失了,取而代之的,则是这三个不同的Material资源。

如有错误,欢迎指出。

email:dxmdxm1992#gmail.com

blog: csdn magicdavid.top
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  unity mesh