Proper way to modify materials at runtime

I’ve use methods like Material.SetColor() at runtime to adjust the emission of certain materials. For example, consider a simple script that causes an object to glow as the player gets closer to it by changing the Emission color of the object’s material:

    public MeshRenderer GlowableMesh;
    private Material _material;

    private GameObject _player;
   
    void Start () {
        _material = GlowableMesh.material;
        _player = GameObject.FindGameObjectWithTag( "Player" );
    }
   
    void Update()
    {
        var dist = Vector3.Distance( transform.position, _player.transform.position );
        var intensity = dist > 20f ? 0 : ((20f - dist) / 200);
        _material.SetColor( "_EmissionColor", new Color( 255 * intensity, 0, 30 * intensity ) );
    }

This works fine in terms of the behavior, but I’m just curious whether it’s reasonable to be adjusting the characteristics of the materials at runtime, from a rendering/batching perspective. Is this the sort of thing that works, but should be avoided, for any reasons?

1 Like

Modifying the parameters of a material at runtime will break batching and add an additional draw call if the object in question was previously being batched.

That’s fine.

Really there’s nothing wrong with what you’re doing.

If it becomes a performance issue for you it might be advisable to look into using MaterialPropertyBlocks instead of directly modifying the material instance. You can modify material settings on a per-renderer component basis, and then undo those changes by setting a null property block allowing the object to return to being possibly batched. This is likely a better practice to use anyway so you might want to look into that.

Another option would be to do this via a shader, but that also kind of guarantees to break batching just by virtue of the material properties needing to be unique unless you’re just fading by surface distance rather than object center.

The other thing you could look into is instancing shaders, which dovetails nicely with MaterialPropertyBlocks. This is really only useful if you’re rendering several dozen of the exact same mesh & texture and want to change non-texture parameters dynamically.

2 Likes

Thanks for the reference to MaterialPropertyBlocks. That’s something I wasn’t familiar with. I’ll look into it.

I think I remember a problem with material parameters being saved to file when you changed them, even from play mode? Or is that just if you change them from the editor?

That’s if you modify the material you get from .sharedMaterial rather than .material, the first returns the actual material asset, the second will make a runtime copy of the material. And yes, the changes are only saved when running in the editor.