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.
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.
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:
public MeshRenderer DoorFrameRenderer;
private Material _doorFrameMaterial;
void Start()
{
_doorFrameMaterial = DoorFrameRenderer.material;
}
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.
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
public MeshRenderer meshRenderer; //Assigned in Editor
public Material materialReference; //Assigned in Editor
private void init()
{
/*this.meshRenderer.materials = new Material[2];
this.meshRenderer.materials[0] = materialReference;
this.meshRenderer.materials[1] = materialReference;*/
this.mat0 = this.meshRenderer.materials[0];
this.mat1 = this.meshRenderer.materials[1];
this.mat0.SetColor("_UnlitColor", Color.red);
this.mat1.SetColor("_UnlitColor", Color.green);
}
Works not
public MeshRenderer meshRenderer; //Assigned in Editor
public Material materialReference; //Assigned in Editor
private void init()
{
this.meshRenderer.materials = new Material[2];
this.meshRenderer.materials[0] = materialReference;
this.meshRenderer.materials[1] = materialReference;
this.mat0 = this.meshRenderer.materials[0];
this.mat1 = this.meshRenderer.materials[1];
this.mat0.SetColor("_UnlitColor", Color.red);
this.mat1.SetColor("_UnlitColor", Color.green);
}
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:
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).
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
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:
public class VaporizingObjectController : MonoBehaviour
{
public Material VaporizedMaterialMaster;
public void OnCollisionEnter(Collision c) {
var renderers = go.GetComponentsInChildren<Renderer>(.ToArray();
foreach (var renderer in renderers)
{
var vaporizedMaterials = new Material[renderer.materials.Length];
for (int matIndex = 0; matIndex < renderer.materials.Length; matIndex++)
{
var originalMaterial = renderer.materials[matIndex];
var vaporizedMat = new Material(VaporizedMaterialMaster);
// Now do whatever you want to vaporizedMat.
vaporizedMaterials[matIndex] = vaporizedMat;
}
renderer.materials = vaporizedMaterials;
}
}
}
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.
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.
I’ll assume you were maybe just assigning it wrong. I use new Material(someOriginalMaterial);, or new Material(someShaderName); in HDRP without any issues.
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.