ShareMaterial

本文探讨了Unity3D中sharedMaterial和material的区别,通过实验解析了它们的内部实现机制。sharedMaterial的修改会影响到所有使用该材质的物体,而material则会克隆材质并只影响当前物体。此外,文章还指出使用material可能导致内存泄漏,需注意手动管理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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的颜色变了 
效果如下图:

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

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;  
  • 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,否则会一直存在内存中。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值