I have a runtime color switcher on a world canvas. I’m trying to access multiple materials on a single mesh. Currently it only is able to modify a single material. I would like to have a public material index where I can drag however many materials in and be able to edit each of them.
For example: My bed mesh has 3 materials. I can currently only modify a single (unchosen) material. I want to be able to modify all 3 materials on the bed mesh based on the public materials that I have dragged into each public material slot.
Here is the script that I’m applying to the bed model:
using UnityEngine;
namespace HSVPicker.Examples
{
public class ColorPickerTester : MonoBehaviour
{
public new Renderer renderer;
public ColorPicker picker;
public Color Color = Color.red;
public bool SetColorOnStart = false;
// Use this for initialization
void Start ()
{
picker.onValueChanged.AddListener(color =>
{
renderer.material.color = color;
Color = color;
});
renderer.material.color = picker.CurrentColor;
if(SetColorOnStart)
{
picker.CurrentColor = Color;
}
}
// Update is called once per frame
void Update () {
}
}
}
I know it’s something to do with render.materials
Thanks!
If those colors are per-instance, I’d use MaterialPropertyBlocks. .color would probably create material instances, which means different SetPasses when it’s ultimately just one material with one property that changes per instance.
Read this.
That article is great for showing how to use material property blocks.
It’s also completely misunderstood by almost every person who reads it! It doesn’t help that the word “instance” is used to mean completely different things with Unity.
Using a MaterialPropertyBlock instead of modifying the material directly means you aren’t changing the parent material or creating a copy, aka a “material instance” in Unity parlance, of that parent material. Property blocks are very lightweight as they’re a list of only the values that are being changed and no more. Where as the Material class is a list of values for all of the properties, in addition to a lot more information than just those properties. You also need to keep track of any Material you create at runtime and manually destroy it, because Unity will not do that for you.
Also note, “material instance” in this case has nothing to do with “instancing”. Instancing being the ability for a GPU to render multiples of the same mesh & material as one draw. That article isn’t even talking about instancing at all, because it predates Unity adding instancing support by a year! There used to be a few unfortunate misunderstandings in the article that caused additional confusion, but which appear to have since been thankfully fixed.
As I mentioned above a “material instance” is just what Unity decided to call a material that was automatically created as a copy of a pre-existing material. It’s equivalent to calling new Materal(myMaterial) in c#, because that’s exactly what it’s doing when you access the renderer.material or renderer.materials* for the first time. Or anytime you use either (access or assign) after assigning .sharedMaterial. Functionally it’s identical to creating a new material from scratch and copying all of the settings, because again that’s exactly what it’s doing. Each unique material will always be a separate “draw call”, even if they have exactly the same settings and properties, even if it’s an “instance”.* Changing the material properties with a property block doesn’t change the fact it’s a separate “draw call”. Assuming the property values are different at least. The performance advantage shown in that link purely comes down to the fact material property blocks are a lot cheaper to deal with on the CPU. A big part of that is because it only changes properties. Material property blocks can’t change anything to do with the shader being used, keywords, or render state (which are sometimes controlled by material properties) which removes a ton of work the CPU has to do when evaluating changing property values on a material normally. That said, you should absolutely use a MaterialPropertyBlock to set the colors instead of modifying the renderer.material directly. ```
*private MaterialPropertyBlock matBlock;
// in the function
if (matBlock == null)
matBlock = new MaterialPropertyBlock(); // just need to make it once and reuse it
for (int i=0; i < renderer.sharedMaterials.Length; i++)
{
matBlock.SetColor(“_Color”, picker.CurrentColor);
renderer.SetPropertyBlock(matBlock, i);
}* * *Once you call SetPropertyBlock``` you can modify the property block and it won’t affect anything you’ve used it on before. It’s just a list of properties that’s being passed along.
I’m a novice at coding and not able to get any of this working. Both provided scripts compile, but I’ll have to figure out how to get either one functioning. Thanks for the right direction!