Instanced material ignoring value changes.

So, I’m working with some objects in my scene that use dynamic materials which are modified at runtime. However, in an attempt to clean up the code some, things have stopped working. I used to instantiate the materials as needed, modify them and set them. That worked fine, but without proper garbage collection that leads to a reasonable memory leak.

Logically speaking, I should only have to instantiate the material once per object, and modify the instance from there. However, all of my attempts to do this lead to Unity simply ignoring any changes at runtime. I have even debugged the values as they change, and they are changing within the material, there is just no visible change. If I modify the values at start, regardless of order, they work. After start, however, anything I do is ignored unless I instantiate another copy, which should not be the case.

    void Start()
	{
		//This works
		Material mat = Instantiate(PosterImage.material);
		mat.SetFloat("_EffectAmount", 0f);
		PosterImage.material = mat;

		//This also works
		PosterImage.material = Instantiate(PosterImage.material);
		PosterImage.material.SetFloat("_EffectAmount", 0f);
	}

Any combination of setting the value and the material in any order within start works perfectly, but if I try to modify that in any way afterwards, it sets the value and does not change anything physically in scene. I would try MaterialPropertyBlocks, but the objects do not have a renderer as they are canvas based. Worst case scenario I will have to go back to instantiating and manually cleaning up the materials, but that is really not something that should be necessary. Is there something I am missing, or is this a limitation of Unity.

Edit: I suppose it helps to mention that the PosterImage variable is an Image, from UnityEngine.UI, so it is governed by a CanvasRenderer as opposed to the standard renderer, as stated above.

Have you tried setting the float on the materialForRendering property instead of the material property?

I don’t quite get your problem. Whenever you acutally use renderer.material, Unity will automatically create an instance of the original material just for this object. Unity does this “once” for each object. There’s no need to instantiate the material manually. If you want to access the shared material (which doesn’t auto-instantiate the material on access) you have to use “sharedMaterial”.

If the value in the material has actually changed but doesn’t affect the actual object, make sure the object is not marked as static because chances are high that the object has been statically batched. Batching in general could be a problem here.

What exactly is “PosterImage”?

ps: Materials are not automatically garbage collected when you loose all references to it. All objects derived from UnityEngine.Object can be recovered by using FindObjectsOfType. They are automatically removed when you change the scene or when you call Resources.UnloadUnusedAssets.

So when you no longer need a dynamically created Material you should Destroy it.