material 和 sharedMaterial 的区别
创建一个Material, 颜色为红色,
创建两个Quad,挂上刚刚创建的材质。 效果如下图:
将第一个Quad挂载如下脚本, 运行:
render = GetComponent<Renderer>();
render.material.color = Color.white;
- 1
- 2
效果如下图:
修改脚本内容如下, 运行:
render = GetComponent<Renderer>();
render.sharedMaterial.color = Color.white;
- 1
- 2
Quad的颜色变了
效果如下图:
如何来解释上面的现象呢, 根据官方文档
sharedMaterial 是共用的 Material,称为共享材质。修改共享材质会改变所用使用该材质的物体,并且编辑器中的材质设置也会改变。
material 是独立的 Material,返回分配给渲染器的第一个材质。修改材质仅会改变该物体的材质。如果该材质被其他的渲染器使用,将克隆该材质并用于当前的渲染器。
推测 sharedMaterial 和 material 的实现
下面我们通过一些实现猜测 material 是如何实现的:
我们从 UnityEngine.Renderer 中可得知 sharedMaterial 和 material 是属性(property),当给属性赋值时会隐式的调用 set 方法,当获取属性值得时候会隐式的调用 get 方法。假设 sharedMaterial 的值变量假设为 _sharedMaterial, material 的值变量假设为 _material。
Material _material;
Material _sharedMaterial;
- 1
- 2
- 第一个测试:
Material origin_sharedMat = render.sharedMaterial;
Debug.Log(origin_sharedMat == render.sharedMaterial);
// True
- 1
- 2
- 3
调用了 sharedMaterial 的 get方法。 可以推断出 sharedMaterial 的 get:
return _sharedMaterial;
- 1
- 第二个测试:
Material new_Mat = new Material(Shader.Find("Standard"));
render.sharedMaterial = new_Mat;
Debug.Log(new_Mat == render.sharedMaterial);
// True
- 1
- 2
- 3
- 4
调用了 sharedMaterial 的 set方法。 可以推断出 sharedMaterial 的 set:
_sharedMaterial = value;
- 1
- 第三个测试:
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
- 1
- 2
- 3
- 4
- 5
- 6
在隐式调用 material 的 get 后, sharedMaterial 被修改。且 _material 和 _sharedMaterial 相等。
推断 material 的 get:
if (_sharedMaterial ~= _material) {
_material = new Material (_sharedMaterial);//这一步由第四个测试共同推出
_sharedMaterial = _material;
}
return _material;
- 1
- 2
- 3
- 4
- 5
- 6
- 第四个测试:
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
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
很明显 _material的初始化并未通过 material 属性,因为未调用 material 属性的 get 方法前,_sharedMaterial 和 _material并不相等。
推断 sharedMaterial 的 set:
_sharedMaterial = value;
- 1
- 第五个测试:
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)
- 1
- 2
- 3
- 4
- 5
- 6
推测 material 的 set:
_sharedMaterial = value;
- 1
综上推断:
public Material material {
get {
if (_sharedMaterial ~= _material) {
_material = new Material (_sharedMaterial);
_sharedMaterial = _material;
}
return _material;
}
set {
_sharedMaterial = value;
}
}
public Material sharedMaterial {
get {
return _sharedMaterial;
}
set {
_sharedMaterial = value;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
使用 material 时的内存泄漏问题
每一次引用 Renderer.material 的时候,都会生成一个新的 material 到内存中去,销毁物体的时候需要我们手动去销毁该material,否则会一直存在内存中。