# Efficiently rendering lots of the same mesh in Unity3D, but with different colors?

I'm working on a tile based game, where grass is spreading from tile to tile, so soon lots of grass appear on the board.

Instancing is on, so the FPS is kind of good, even with 300k triangles (1 grass leaf consists of 90 triangles).

BUT: Tiles can be wet and dry on floating scale, which makes the grass leaves turn from green to yellow.

Currently I implemented this simply as such:

var r = originalColor.r + 0.27f * (1f - (ground.Status / 100));
renderer.material.color = new Color(r, originalColor.g, originalColor.b);


Which creates a new material for every grass...

Obviously I can't change the sharedMaterial's color, because that would change all other grass' colors as well.

So how could I efficiently render grass on a big spectrum from yellow to green?

One thing I came up with is that I create only like 100 materials, with 1 percent steps.

• = first material is 100% yellow, and the last one is 100% green.

And when a grass turns a bit more green, it doesn't have to create a whole new material with 3.47% green, but instead can use the already created 3% green material.

Any better ideas?

Thanks!

• Presumably you considered encoding your moisture level in a texture map your material can sample based on worldspace position? May 19 '20 at 16:02
• Hmm, that's a good idea as well. But I don't quite get how could I sample the correct color based on worldspace position. What I was thinking is that I would sample it based on a moisture value, which is passed to the shader as well. Maybe I can set that value quickly without big performance overheads. May 19 '20 at 16:07
• Take a look at docs.unity3d.com/ScriptReference/MaterialPropertyBlock.html, I believe its far more performant than creating a new material instance for each bit of grass. May 19 '20 at 19:31
• @Pheonix2105 Woah, this looks great, just what I needed, I will get back to you as soon as I could test is. May 20 '20 at 6:12

MaterialPropertyBlocks are perfect for this situtation.

I followed a quick tutorial, and managed to boost the performance by a lot.

The behaviour below always passes the Color stored in its MaterialColor property to the GPU via a MaterialPropertyBlock, thus letting instanced meshes have different i.e. colors.

public class ColorPropertySetter : MonoBehaviour
{
//The color of the object
public Color MaterialColor;

//The material property block we pass to the GPU
private MaterialPropertyBlock propertyBlock;

private Renderer renderer;

void Start()
{
//create propertyblock
propertyBlock = new MaterialPropertyBlock();
//Get a renderer component either of the own gameobject or of a child
renderer = GetComponent<Renderer>();
//set the color property
propertyBlock.SetColor("_Color", MaterialColor);
//apply propertyBlock to renderer
renderer.SetPropertyBlock(propertyBlock);
}

void Update()
{
//create propertyblock
propertyBlock = new MaterialPropertyBlock();
//set the color property
propertyBlock.SetColor("_Color", MaterialColor);
//apply propertyBlock to renderer
renderer.SetPropertyBlock(propertyBlock);
}
}


The shader below uses the given MaterialPropertyBlock and also instancing.

Shader "Tutorial/048_Instancing" {
//show values to edit in inspector
Properties{
[PerRendererData] _Color ("Color", Color) = (0, 0, 0, 1)
}

//the material is completely non-transparent and is rendered at the same time as the other opaque geometry
Tags{ "RenderType"="Opaque" "Queue"="Geometry"}

Pass{
CGPROGRAM
//allow instancing
#pragma multi_compile_instancing

#pragma vertex vert
#pragma fragment frag

#include "UnityCG.cginc"

//per vertex data that comes from the model/parameters
struct appdata{
float4 vertex : POSITION;
UNITY_VERTEX_INPUT_INSTANCE_ID
};

//per vertex data that gets passed from the vertex to the fragment function
struct v2f{
float4 position : SV_POSITION;
UNITY_VERTEX_INPUT_INSTANCE_ID
};

UNITY_INSTANCING_BUFFER_START(Props)
UNITY_DEFINE_INSTANCED_PROP(float4, _Color)
UNITY_INSTANCING_BUFFER_END(Props)

v2f vert(appdata v){
v2f o;

//setup instance id
UNITY_SETUP_INSTANCE_ID(v);
UNITY_TRANSFER_INSTANCE_ID(v, o);

//calculate the position in clip space to render the object
o.position = UnityObjectToClipPos(v.vertex);
return o;
}

fixed4 frag(v2f i) : SV_TARGET{
//setup instance id
UNITY_SETUP_INSTANCE_ID(i);
//get _Color Property from buffer
fixed4 color = UNITY_ACCESS_INSTANCED_PROP(Props, _Color);
//Return the color the Object is rendered in
return color;
}

ENDCG
}
}

• The problem with links is that they tend to break. Can you perhaps add a code example for how to use MaterialPropertyBlocks to your answer? May 22 '20 at 16:43
• It also seems like material property blocks are just a very small part of the article you linked. One more reason to do your readers a favor by extracting the relevant knowledge from the article and explaining it in your own words. May 22 '20 at 16:54
• @Philipp Thanks, edited :) May 22 '20 at 17:56