Awesome
Material Property Provider
Problem Statement
Making Renderers use unique values, without manually creating Material Variants
Requires handling Material Property Blocks, and/or Material Instances (at Runtime only).
Assigning values from a MonoBehaviour
Requires setting the Material Property Block or Material's properties on Start()
, OnValidate()
, Reset()
, and Update()
.
SRP Batcher Compatibility
Requires using Material Property Blocks when in Edit Mode (not to leak Materials in the Editor), but unique Material Instances at Runtime.
Solution
A MonoBehaviour based class that will automatically set its Renderer's Material Properties built from fields and properties marked with MaterialProperty Attribute.
Derive from MaterialPropertyProviderBase
and decorate Fields and Properties with [MaterialProperty("_Reference")]
Attributes to add them to the Material Properties that will automatically be handled.
Supported Types are: bool, float, int, Color, Vector2, Vector3, Vector4, Matrix4x4, Texture, Texture2D, Texture3D, Cubemap, RenderTexture.
Any other type will issue a warning and just be discarded.
SRP Batcher Compatibility
When using URP or HDRP with SRP Batcher Enabled, Material Property Blocks will only be used when in Edit Mode. Runtime / Play Mode will then make unique Material instances. Make sure your Materials are compatible with the SRP Batcher.
Example
Radial Gradient
[ExecuteAlways] // this allows for animated properties preview in Timeline when in Edit Mode
public class RadialGradientPropertyProvider : MaterialPropertyProviderBase
{
#pragma warning disable CS0414
[SerializeField, ColorUsage(true, true), MaterialProperty("_ColorA")] Color _colorA = Color.yellow;
[SerializeField, ColorUsage(true, true), MaterialProperty("_ColorB")] Color _colorB = Color.cyan;
[SerializeField, MaterialProperty("_Center")] Vector2 _center;
[SerializeField, MaterialProperty("_Radius")] float _radius = .5f;
[SerializeField, Range(.1f, 5f), MaterialProperty("_Power")] float _power = 1f;
[SerializeField] bool _useTexture;
[SerializeField, MaterialProperty("_Texture")] Texture _texture;
#pragma warning restore CS0414
[MaterialProperty("_UseTexture")]
public bool UseTexture { get => _useTexture && _texture != null; }
Renderer[] _renderers;
protected override Renderer[] renderers // <-- passing the renderers to work with
{
get
{
if (_renderers == null)
_renderers = new Renderer[1] { GetComponent<Renderer>() };
return _renderers;
}
}
}
The component will automatically set the fields' and properties' values in the Renderer's Material Properties.