Search Unity

  1. We are migrating the Unity Forums to Unity Discussions. On July 12, the Unity Forums will become read-only. On July 15, Unity Discussions will become read-only until July 18, when the new design and the migrated forum contents will go live. Read our full announcement for more information and let us know if you have any questions.

Resolved Copy of HDRP materials in code

Discussion in 'High Definition Render Pipeline' started by L_tD, Nov 3, 2020.

  1. L_tD

    L_tD

    Joined:
    Mar 8, 2017
    Posts:
    39
    Hi,

    sorry for such a simple question, maybe therefore I didn't found anything about it in the forum. I try to make a "copy" of an given HDRP/unlit material in code.

    I tried it with

    Code (CSharp):
    1. this.meshRenderer.materials[0] = Instantiate(this.materialReference);
    or

    Code (CSharp):
    1. this.meshRenderer.materials[0] = new Material(this.materialReference);
    And I see no inherited class from Material.

    Problem is that it will not create a new instance of my unlit material, it will create a "DefaultHDMaterial (Instance)", but this is not what I want. I want an copy of my HDR/unlit material, which is one of the standard materials.

    I'm using unity 20.20.1 and HDRP 7.1.2. I hope someone can help me, feeling really stupid about this question, sorry.

    Best
    L_tD
     
  2. L_tD

    L_tD

    Joined:
    Mar 8, 2017
    Posts:
    39
    Tried some other things like:

    Code (CSharp):
    1. Material mat = new Material(Shader.Find("HDRP/Unlit"));
    But still no success. I get always "DefaultHDMaterial (Instance)"
     
  3. dgoyette

    dgoyette

    Joined:
    Jul 1, 2016
    Posts:
    4,215
    How are you using this material? Simply accessing the material of a renderer, at runtime, will create a copy of the original material. It's fairly common for me to have some renderers override their material properties dynamically. In those cases, I just do something like this:

    Code (CSharp):
    1. public MeshRenderer DoorFrameRenderer;
    2. private Material _doorFrameMaterial;
    3.  
    4. void Start()
    5. {
    6.     _doorFrameMaterial = DoorFrameRenderer.material;
    7. }
    That will cause the DoorFrameRenderer to create a copy of its material (an "Instance" of the material") automatically.
    _doorFrameMaterial
    now contains a reference to that material, so I can adjust shader settings on the material and only affect that one door frame renderer.

    Are you trying to do something similar to that?
     
  4. L_tD

    L_tD

    Joined:
    Mar 8, 2017
    Posts:
    39
    Hi,

    thanks for answering, thats a good step in the right direction. Thanks a lot!

    If I do it like you wrote, it works.

    But I try to set dynamically in code how many materials a Meshrenderer has, depending on the submeshes.
    Therefore I tried to set my HDRP/Unlit material as reference in the script and tried to determine how many mterials i need.This is currently not working.

    Works
    Code (CSharp):
    1. public MeshRenderer meshRenderer; //Assigned in Editor
    2. public Material     materialReference; //Assigned in Editor
    3.  
    4. private void init()
    5.     {
    6.         /*this.meshRenderer.materials = new Material[2];      
    7.         this.meshRenderer.materials[0] = materialReference;
    8.         this.meshRenderer.materials[1] = materialReference;*/
    9.  
    10.         this.mat0 = this.meshRenderer.materials[0];
    11.         this.mat1 = this.meshRenderer.materials[1];
    12.  
    13.         this.mat0.SetColor("_UnlitColor", Color.red);
    14.         this.mat1.SetColor("_UnlitColor", Color.green);
    15.     }

    Works not

    Code (CSharp):
    1. public MeshRenderer meshRenderer; //Assigned in Editor
    2. public Material     materialReference; //Assigned in Editor
    3.  
    4. private void init()
    5.     {
    6.         this.meshRenderer.materials = new Material[2];      
    7.         this.meshRenderer.materials[0] = materialReference;
    8.         this.meshRenderer.materials[1] = materialReference;
    9.  
    10.         this.mat0 = this.meshRenderer.materials[0];
    11.         this.mat1 = this.meshRenderer.materials[1];
    12.  
    13.         this.mat0.SetColor("_UnlitColor", Color.red);
    14.         this.mat1.SetColor("_UnlitColor", Color.green);
    15.     }
    But maybe with your help I will find a way.

    Thanks and best
    L_tD
     
  5. dgoyette

    dgoyette

    Joined:
    Jul 1, 2016
    Posts:
    4,215
    I don't think you're supposed to try to assign individual sub-materials to the "materials" array by index. Instead, you're supposed to assign the entire materials array back to the renderer in one shot. So, maybe try something like this:

    Code (CSharp):
    1. private Material[] _materials;
    2.  
    3. private void init()
    4. {
    5.     _materials = this.meshRenderer.materials;
    6.     materials[0] = materialReference;
    7.     materials[1] = materialReference;
    8.     this.meshRenderer.materials = _materials;
    9. }
    I'm also not 100% sure whether
    this.mat0 = this.meshRenderer.materials[0];
    makes a copy/instance of the material in the same way that
    this.meshRenderer.material
    does. You should verify that. It's possible that [0] and [1] are the same reference the way you're written the code, and that they're therefore have the same color (whichever color you assign to it last).
     
  6. L_tD

    L_tD

    Joined:
    Mar 8, 2017
    Posts:
    39
    I tried it in that way

    Code (CSharp):
    1. private void init()
    2.     {
    3.         var matRef = this.meshRenderer.materials;
    4.  
    5.         matRef[0] = this.materialReference;
    6.         matRef[1] = this.materialReference;
    7.         matRef[0].SetColor("_UnlitColor", Color.red);
    8.         matRef[1].SetColor("_UnlitColor", Color.green);
    9.  
    10.         this.meshRenderer.materials = matRef;
    11.  
    12.     }
    But then both materials are gree, which is clear because both have set the same material reference.

    The other problem is, I want to define the size of the materials array inside the meshRenderer, because I try to create a dynamic particle system where you can add points which are all stored in the same mesh, but for each different color I use the submeshes. Sounds for me ok, instead of doing different me
     
  7. dgoyette

    dgoyette

    Joined:
    Jul 1, 2016
    Posts:
    4,215
    If you want to replace a renderer's existing materials with newly instantiated versions from some reference, that's simple. Consider this script I have that "vaporizes" objects that touch it, replacing a renderer's existing material with a new instance of a reference material:

    Code (CSharp):
    1. public class VaporizingObjectController : MonoBehaviour
    2. {
    3.     public Material VaporizedMaterialMaster;
    4.    
    5.     public void OnCollisionEnter(Collision c) {  
    6.         var renderers = go.GetComponentsInChildren<Renderer>(.ToArray();
    7.         foreach (var renderer in renderers)
    8.         {
    9.             var vaporizedMaterials = new Material[renderer.materials.Length];
    10.  
    11.             for (int matIndex = 0; matIndex < renderer.materials.Length; matIndex++)
    12.             {
    13.                 var originalMaterial = renderer.materials[matIndex];
    14.                 var vaporizedMat = new Material(VaporizedMaterialMaster);
    15.                
    16.                 // Now do whatever you want to vaporizedMat.
    17.  
    18.                 vaporizedMaterials[matIndex] = vaporizedMat;
    19.             }
    20.  
    21.             renderer.materials = vaporizedMaterials;
    22.         }
    23.     }  
    24. }
    I don't know how to do that. What does it even mean to programmatically change the number of materials on a mesh renderer? How would any given face of the renderer know what material it's associated with? I don't know how to do that.
     
  8. L_tD

    L_tD

    Joined:
    Mar 8, 2017
    Posts:
    39
    Thanks, I will try to do this, but I fear I come back to my general problem.

    In my case the VaporizedMaterialMaster would be my HDRP/Unlit material and the copy constructor

    new Material(VaporizedMaterialMaster) will create no new instance of HDRP/unlit it will create "DefaultHDMaterial (Instance)" and this is not what I want. Using the Material copy constructor was the second attempt I did if you see my first post in this thread.
     
  9. dgoyette

    dgoyette

    Joined:
    Jul 1, 2016
    Posts:
    4,215
    I'll assume you were maybe just assigning it wrong. I use
    new Material(someOriginalMaterial);
    , or
    new Material(someShaderName);
    in HDRP without any issues.
     
  10. L_tD

    L_tD

    Joined:
    Mar 8, 2017
    Posts:
    39
    Ok, hmm strange. OK will try it again. Thanks a lot for helping and let you know if it will work or not

    Best
    L_tD
     
  11. L_tD

    L_tD

    Joined:
    Mar 8, 2017
    Posts:
    39
    Cools works, many thanks!

    I guess my problem was, that I did this:
    Code (CSharp):
    1. public MeshRenderer meshRenderer; //Assigned in Editor
    2. public Material     materialReference; //Assigned in Editor
    3. private void init()
    4.     {
    5.         this.meshRenderer.materials = new Material[2];    
    6.         this.meshRenderer.materials[0] = materialReference;
    7.         this.meshRenderer.materials[1] = materialReference;
    8.         this.mat0 = this.meshRenderer.materials[0];
    9.         this.mat1 = this.meshRenderer.materials[1];
    10.         this.mat0.SetColor("_UnlitColor", Color.red);
    11.         this.mat1.SetColor("_UnlitColor", Color.green);
    12.     }
    Means I created arry first and then worked with the array in the mesh renderer.

    If I do it that way, it works:

    Code (CSharp):
    1. private void init()
    2.     {
    3.         var matRef = new Material[2];
    4.         matRef[0] = new Material(this.materialReference);
    5.         matRef[1] = new Material(this.materialReference);
    6.  
    7.         this.mat0 = matRef[0];
    8.         this.mat1 = matRef[1];
    9.  
    10.         this.mat0.SetColor("_UnlitColor", Color.red);
    11.         this.mat1.SetColor("_UnlitColor", Color.green);
    12.  
    13.         this.meshRenderer.materials = matRef;
    14.  
    15.     }
    Many thanks for your help, I fear I needed weeks until I find out.

    Best
    L_tD
     
  12. dgoyette

    dgoyette

    Joined:
    Jul 1, 2016
    Posts:
    4,215
    Yeah, there is definitely a specific specific way to do it, which isn't obvious at all. It all seems to come down to that final assignment, where you you assign the local array to
    meshRenderer.materials
    . Maybe if we could look at the underlying code we'd find that something special happens during the 'set' operation on the
    material
    and
    materials
    properties which just doesn't happen when indexing into those properties.

    Anyway, glad it all finally came together,