Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Resolved Is there a way to temporary disable a material?

Discussion in 'General Graphics' started by Tom-Kazansky, Sep 17, 2021.

  1. Tom-Kazansky

    Tom-Kazansky

    Joined:
    Mar 9, 2015
    Posts:
    58
    Hello,
    I have a model that use Skinned Mesh Renderer and there are two materials for this:
    - default material of the model (lets call this "default material")
    - material with shader effect (lets call this "effect material")

    at runtime, I clone the effect material by using new Material( effect material ), then assign that clone to the Renderer whenever I need that effect, but everytime I did that, it seems Unity create a new instance of that material (I have set enableInstancing = false, I'm not sure if it did what I think it did), is this a waste of resource? is there anyway I can prevent Unity from instantiating new material like this?

    I also think about another solution: put both "default material" and "effect material" in the Renderer, but I have no ideas how to "disable" one of them.

    anyone know a solution for my problem?

    I'm using Unity 2020.3.8f and my project has URP.
    Thanks for reading.
     
  2. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,329
    Why? Unless you need to modify the material directly, there's no reason to make a new one. Even then most of the time you can modify the material properties using material properties blocks without having to create a new material, though there are currently issues with doing that on the URP.

    Completely unrelated. Unity's choice to rename copies of a material "material name (instance)" was unfortunate, as it's not an "instance" in the way that term has come to mean. It's just a copy of the material. These days the term "instance" usually refers to GPU Instancing, which is what that
    mat.enableInstancing
    is related to.

    Doing
    new Material(material)
    also makes a new material that copies the properties of an existing material. Once created it has no connection to the original it was copied from, and may or may not support GPU Instancing (which is determined primarily by the shader being used). If you assign a material to a renderer using
    rend.material
    or
    rend.materials[index]
    it also makes a copy of the material that you're assigning. It'll make a copy every time you do that!

    So if you're doing
    new Material(material)
    and assigning the material using
    rend.material
    you're making two new materials.

    So if you have an existing material you want to use, and not make a new copy, use
    rend.sharedMaterial
    or
    rend.sharedMaterials[index]
    for both assigning and accessing the material.

    Presumably you have a script that has a reference to the effect material on the object you want to apply it to. My recommendation is also have a reference to the original material, or cache it in your script.
    Code (csharp):
    1. public Material effectMaterial;
    2.  
    3. private Renderer rend;
    4. // might need to be an array if you need to support multiple material indicies on one object
    5. private Material originalMaterial;
    6.  
    7. void Start()
    8. {
    9.     // assuming you don't already have this as a public variable
    10.     rend = GetComponent<Renderer>();
    11.  
    12.     // copy reference to the original material
    13.     originalMaterial = rend.sharedMaterial;
    14. }
    15.  
    16. void OnEnable()
    17. {
    18.     rend.sharedMaterial = effectMaterial;      
    19. }
    20.  
    21. void OnDisable()
    22. {
    23.     rend.sharedMaterial = originalMaterial;
    24. }
    If you need to modify the effect material properties, make a copy with
    effectMaterial = new Material(effectMaterial)
    in
    Start()
    so you only ever make one copy per-object.
     
  3. Tom-Kazansky

    Tom-Kazansky

    Joined:
    Mar 9, 2015
    Posts:
    58
    well... I created a new instance of the material so I can swap it out many times without creating new instance, but Unity keeps creating new instance (even if I assign the cloned instance)

    might be clearer if there is some code:

    Code (CSharp):
    1.    
    2.     public Material effectMaterial; // reference to the original effect material
    3.  
    4.     public Material effectMaterialClone;
    5.     private Renderer rend;
    6.  
    7.     private Material originalMaterial;
    8.  
    9.     void Start()
    10.     {
    11.         // I clone it because I think creating new instance every assignment is "not good"
    12.         effectMaterialClone = new Material(effectMaterial);
    13.      
    14.         // reference to the renderer
    15.         rend = GetComponent<Renderer>();
    16.  
    17.         // save reference to the original material
    18.         originalMaterial = rend.material;
    19.     }
    20.  
    21.     void ShouldTurnOnEffect()
    22.     {
    23.         // I thought Unity would use my material instance but Unity created new instance instead
    24.         rend.material = effectMaterialClone;
    25.         // a new instance is created eveytime I do this, will this affect performance in anyways?
    26.         // I don't do this every frame, though.
    27.     }
    28.  
    29.     void ShouldGoBack()
    30.     {
    31.         rend.material = originalMaterial; // new instance of originalMaterial is created
    32.     }
    33.  
    ---
    about "disable" thing I mentioned, I would assign multiple materials to my model, like this:
    material-list.png
    is there anyway that I can disable element 1, element 2 in the materials list?
    and then when I need "Petrify", I would disable element 0, element 2, the same apply to "Dissolve".
     
  4. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,329
    Yes.
     
  5. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,329
    Nope, you can't do that.

    You keep the list of materials in the script and swap between them, using
    rend.sharedMaterial
    . Just not using
    rend.material
    , because that explicitly makes a new copy every time.
     
    Tom-Kazansky likes this.
  6. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,329
  7. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,329
    One more note:
    That's not what that line does. That saves a copy of the original material, not the actual original material. Because
    rend.material
    makes a copy of the material!
     
    Tom-Kazansky likes this.