- Home /
Changing material properties on one object changes all others
I have a fadeout script to change the material properties when something blocks the screen. I want the objects to fade individually but when a new object fades, all the others also start the fade again or start fully faded out immediately.
The Docs say changing material properties using Renderer.material creates a new instance of the material, but the behaviour I see points to them still being the same instance.
The material is a simple unlit shadergraph and here's the Coroutine I use to fade the object:
IEnumerator TransitionTransparency() {
_renderer.material = _transparentMat;
float transition = 1;
while (transition > 0) {
_renderer.material.SetFloat("_Transition", transition);
transition -= Time.deltaTime / _transitionTime;
yield return null;
}
Destroy(_renderer.material);
_renderer.material = _transparentMat;
yield break;
}
Answer by HeilBoomer · May 10 at 05:51 PM
I'd recommend Material Property Blocks. It is a class that you can set to almost every renderer and it will override the renderer's shader properties.
For Instance, here is a script that I wrote for tweaking the "_Fade" property using DOTween.
using DG.Tweening;
using UnityEngine;
using UnityEngine.Events;
public class Interactable : MonoBehaviour, IRaycastEnter
{
[SerializeField] protected MeshRenderer mR;
protected MaterialPropertyBlock mPB;
protected Vector3 StartScale;
protected float tw = 0;
protected virtual void Awake()
{
mPB = new MaterialPropertyBlock();
}
// This isn't the whole script but I think that would be enough to show how it works.
public virtual void OnRaycastEnter()
{
if (!interactable) return;
//throw new System.NotImplementedException();
if (flashTW != null) flashTW.Kill();
flashTW = DOTween.To(() => tw, x => tw = x, 1, .22f)
.OnUpdate(() =>
{
mR.transform.localScale = StartScale * (1 + (tw * scaleMultiplier));
// This is the Material Property Part, first you get the property block from the renderer.
mR.GetPropertyBlock(mPB);
//Then you tweak the values in the Material Property Block
mPB.SetFloat("_Fade", tw);
//Finally you set the property block of the renderer
mR.SetPropertyBlock(mPB);
});
}
}
Thanks, Tried it out and this solution works even without setting the override. I rewrote my code to use DOTween in the meantime (forgot I had it in the project) and for a single property it seems simpler. I suppose the upside of using your solution (apart from being what unity is pushing towards, I guess) is that fewer draw calls are made, so it definitely has merit when many objects are changed. Anyways, thanks for pointing out a feature I was unaware of.
Answer by consumedman · May 10 at 05:37 PM
So after trying out a few things I found the solution. Leaving it here for anyone having a similar issue in the future.
The problem is with how shadergraph shaders handles non-exposed properties by default. If you want to have a non-exposed property that can be changed for every material instance you have to enable "Override property declaration" and set "Shader Declaration" to "per material" in the property settings.
Your answer
