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.
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.
public Material effectMaterial;
private Renderer rend;
// might need to be an array if you need to support multiple material indicies on one object
private Material originalMaterial;
void Start()
{
// assuming you don't already have this as a public variable
rend = GetComponent<Renderer>();
// copy reference to the original material
originalMaterial = rend.sharedMaterial;
}
void OnEnable()
{
rend.sharedMaterial = effectMaterial;
}
void OnDisable()
{
rend.sharedMaterial = originalMaterial;
}
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.
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:
public Material effectMaterial; // reference to the original effect material
public Material effectMaterialClone;
private Renderer rend;
private Material originalMaterial;
void Start()
{
// I clone it because I think creating new instance every assignment is "not good"
effectMaterialClone = new Material(effectMaterial);
// reference to the renderer
rend = GetComponent<Renderer>();
// save reference to the original material
originalMaterial = rend.material;
}
void ShouldTurnOnEffect()
{
// I thought Unity would use my material instance but Unity created new instance instead
rend.material = effectMaterialClone;
// a new instance is created eveytime I do this, will this affect performance in anyways?
// I don't do this every frame, though.
}
void ShouldGoBack()
{
rend.material = originalMaterial; // new instance of originalMaterial is created
}
about “disable” thing I mentioned, I would assign multiple materials to my model, like this:
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”.
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.
That’s not what that line does. That saves a copy of the original material, not the actual original material. Because rend.materialmakes a copy of the material!