sharedMaterial和material的区别

Posted 僵师先森

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了sharedMaterial和material的区别相关的知识,希望对你有一定的参考价值。

区别点

  1. sharedMaterial表示共享材质,修改共享材质会改变所有使用该材质的物体,并且编辑器中的材质设置也会变

  2. material表示材质实例,修改材质仅会改变物体的材质,如果该材质被其他渲染器使用,将克隆该材质用于当前的渲染器

  3. 每次引用Renderer.material的时候,会生成一个新的material在内存当中,需要注意内存泄漏。销毁物体的时候需要手动销毁material(Destroy(material)),或者在切换场景的时候调用Resources.UnloadUnusedAssets也可以删除该材质。

实例化出的Material无法使用Resources.UnloadAsset()销毁,因为Resource.UnloadAsset是卸载asset资源,实例化出的Material需要通过Destory来销毁。

        4.调用Renderer.sharedMaterials 相同的两个材质球 ,返回的也是两个,如下图,

 

源码解析

源码说明

Renderer会有一个m_Materials数组,Instantiate的GameObject的Renderer组件中m_Materials引用了materials依赖资源

sharedMaterials属性的get是拿到m_Materials,sharedMaterials属性的get是传入参数0 拿到m_Materials[0] 第一个材质

materials属性的get是会Instantiate一份m_Materials出来,并且将m_Materials = (Instantiate)m_Materials,material属性的get是拿到的是重新赋值后的m_Materials[0]

伪代码

class Renderer

    Material[] m_Materials;  //GameObject Instantiate出来之后 他是引用了依赖的Material
    
    Material[] sharedMaterials
    
        get
            return m_Materials;
        
        set
            m_Materials = value;
        
    
    
    Material sharedMaterial
    
        get
           return m_Materials[0];
        
        set
            m_Materials[0] = value;
        
    
    
    Materials[] materials
    
        get
            Material[] instMats = new Material[m_Materials.length];
            for(int i=0;i<m_Materials.Length;i++)
            
                Material newMat = 实例化m_Material[i]出来
                instMats[i] = newMat;  
                m_Materials[i] = newMat;   //注意后面sharedMaterials拿的是重新赋值后的
            
            return instMats;
        
        set
            m_Materials[0] = value; 
        
    
    
    Material material
    
        get
            Material instMat = 实例化m_Material[0]出来
            m_Materials[0] = instMat;    //注意后面sharedMaterial拿的是重新赋值后的
            return instMat;
        
        set
             m_Materials[0] = value;
        
    

实际运用注意点(优化点)

  1. material字段会产生新的资源,要注意销毁

  2. 改变sharedMaterial不会产生内存消耗,但是会把内存中原始的Material资源更改掉

  3. //我们需要改变共享材质,但是在编辑器模式下 我们不想原始资源频繁被改动 可以这样子
    public static Material GetMaterial(Renderer render)  
          
    #if UNITY_EDITOR  
            return render.material;  
    #else  
            return render.sharedMaterial;  
    #endif  
          
  4. 如果是主角这一类gameobject身上需要修改材质的属性或者shader属性比较多的时候,可以第一次使用material,这样可以动态的生成一个material实例,然后再使用sharedmaterial,动态的修改这个新生成的material,而且不会创建新的material

  5. 一般情况下,资源属性的改变情况都是固定的,并非随机出现。比如,假设GameObject受到攻击时,其Material属性改变随攻击类型的不同而有三种不同的参数设置。那么,对于这种需求,我们建议你直接制作三种不同的Material,在Runtime情况下通过代码直接替换对应GameObject的Material,而非改变其Material的属性。

但是注意动态替换Material sharedMaterials[0] = 某个材质 是不生效的 正确使用如下

Renderer psr = gameObject.GetComponent<Renderer>();
​
psr.sharedMaterials[0] = newMat  //错误 不会生效 
​
//正确赋值
Material[] arrMat = psr.sharedMaterials;
arrMat[0] = newMat;
psr.sharedMaterials = arrMat;
​
//单独赋值material也是生效的
psr.sharedMaterial = newMat
​
//解析:
//sharedMaterials的get方法返回的引用是const修饰的常引用,可能这个造成了无法修改  或者是sharedMaterials的get方法返回的一份拷贝的材质数组
//sharedMaterial拿到的是m_Materials数组的第一个元素
//二者set的方法都是对数组进行重新赋值

参考链接

Mesh中 material 和 sharedMaterial 的区别及内部实现的推断 (看了源码,作者推断的是正确的)

赋值新材质无效问题记录

Unity里面获取Material里面的所有Texture

关于material和sharedMaterial的问题

在unity3d中,Renderer组件有两个属性:material和sharedMaterial,它们都可以用来获取Renderer的材质属性。但是它们之间却又很大的区别,下面通过示例来讲解一下。

准备工作:unity3d中新建一个空场景;创建两个cube,分别命名为Cube0、Cube1;在Project中新建一个材质球,取名M0,shader选择Unlit/Color,shader的Main Color属性设为白色,即(255,255,255,255);将M0分别赋值给Cube0、Cube1;新建一个脚本TestMaterial,将脚本拖到Cube0上。如下所示:

1、测试material

TestMaterial脚本的内容如下所示:

 1 using UnityEngine;
 2 using System.Collections;
 3 
 4 public class TestMaterial : MonoBehaviour
 5 {
 6     Renderer thisRenderer;
 7     // Use this for initialization
 8     void Start()
 9     {
10         thisRenderer = GetComponent<Renderer>();
11         thisRenderer.material.color = Color.red;
12     }
13 }

运行,效果如下图所示:

注意,只有Cube0的颜色改变,在mesh renderer中,材质球的名字是M0(Instance)。

2、测试sharedMaterial

代码如下:

 1 using UnityEngine;
 2 using System.Collections;
 3 
 4 public class TestMaterial : MonoBehaviour
 5 {
 6     Renderer thisRenderer;
 7     // Use this for initialization
 8     void Start()
 9     {
10         thisRenderer = GetComponent<Renderer>();
11         thisRenderer.sharedMaterial.color = Color.red;
12     }
13 }

运行,效果如下所示:

注意,Cube0、Cube1的颜色均发生改变,但是在mesh renderer中,材质球的名字还是M0。但是,此时在Project中的M0的Main Color属性发生了改变,如下所示:

 

总结:使用material属性的时候,每次都会new一份新的material作用与它,但不会改变本地工程中的材质material;sharedMaterial是共享材质,无论使用多少次,内存中都只会占用一份内存,但是会影响其他使用同一材质球的对象。所以,从效率上来说,sharedMaterial的效率更高。

 

重点来了:在实际使用的时候,我们可以在开始的时候使用material产生一个新的材质球作用于该renderer,然后之后的操作都使用sharedMaterial,这样,效率更高,而且不会影响其他使用同一个材质球的renderer。

最后,附上示例代码:

 

 1 using UnityEngine;
 2 using System.Collections;
 3 
 4 public class TestMaterial : MonoBehaviour
 5 {
 6     Renderer thisRenderer;
 7     float delay = 3f;
 8     float changeColorTm;
 9     bool isChangeOnce;
10 
11     void Awake()
12     {
13         thisRenderer = GetComponent<Renderer>();
14         thisRenderer.material.color = Color.red;
15         changeColorTm = Time.time + delay;
16         isChangeOnce = false;
17     }
18 
19     void Update()
20     {
21         if (!isChangeOnce && Time.time >= changeColorTm)
22         {
23             isChangeOnce = true;
24             thisRenderer.sharedMaterial.color = Color.green;
25         }
26     }
27 }

 

以上是关于sharedMaterial和material的区别的主要内容,如果未能解决你的问题,请参考以下文章

sharedMaterial和material的区别

关于material和sharedMaterial的问题

Unity --- sharedMaterial material

Unity_Renderer 中(sharedMaterial 和Miaterial)

unity, 不要change Default sharedMaterial

以下unity3d的C#代码是啥意思?