1
\$\begingroup\$

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!

| improve this question | | | | |
\$\endgroup\$
  • \$\begingroup\$ Presumably you considered encoding your moisture level in a texture map your material can sample based on worldspace position? \$\endgroup\$ – DMGregory May 19 at 16:02
  • \$\begingroup\$ 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. \$\endgroup\$ – Tudvari May 19 at 16:07
  • 1
    \$\begingroup\$ 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. \$\endgroup\$ – Pheonix2105 May 19 at 19:31
  • \$\begingroup\$ @Pheonix2105 Woah, this looks great, just what I needed, I will get back to you as soon as I could test is. \$\endgroup\$ – Tudvari May 20 at 6:12
0
\$\begingroup\$

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)
}

SubShader{
    //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

        //shader functions
        #pragma vertex vert
        #pragma fragment frag

        //use unity shader library
        #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
    }
}

Source: https://www.ronja-tutorials.com/2020/02/11/material-property-blocks.html

| improve this answer | | | | |
\$\endgroup\$
  • \$\begingroup\$ 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? \$\endgroup\$ – Philipp May 22 at 16:43
  • \$\begingroup\$ 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. \$\endgroup\$ – Philipp May 22 at 16:54
  • \$\begingroup\$ @Philipp Thanks, edited :) \$\endgroup\$ – Tudvari May 22 at 17:56

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.