DEV Community

Daniel Chou Rainho
Daniel Chou Rainho

Posted on

[Unity] GameObject to Shader Communication: MaterialPropertyBlocks

Intro

Each GameObject in my scene is given a unique ID, and I want this ID value to be used inside the Shader that is part of the Material of every GameObject in my scene.

Specifically, I want this identifier to be able to be stored in a Color channel, i.e. it has to be a float between 0 and 1 that is unique to every GameObject in the scene.

Architecture

In the following 3 sections I outline the 3 main scripts I used for this post.

They interact as follows:

  1. My Identification script is attached to each GameObject in the scene that I want to attribute to an unique ID.
  2. This Identification script calls a function from the script IDManager (which I have attached to another GameObject in my scence) to get a unique ID.
  3. Then, the Identification script creates a MaterialPropertyBlock object to which it assigns the ID.
  4. Finally, in my Shader which I assign to the material associated with a custom Render Objects Render Feature, I can read this ID value from the MaterialPropertyBlock to then return it in the red color channel in the fragment shader.

IDManager

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

/**
* The first id starts at 0.0001.
* We don't use 0 because that's reserved for the background.
*/

public class IDManager : MonoBehaviour
{
    public static IDManager instance; // Singleton instance
    private int lastAssignedID = 0;  // Keep track of last assigned ID
    private int totalObjects = 10000; // Total number of objects you expect

    void Awake()
    {
        // Singleton setup
        if (instance == null)
        {
            instance = this;
            DontDestroyOnLoad(gameObject); // Ensure the manager persists between scenes
        }
        else
        {
            Destroy(gameObject);
        }
    }

    public float GetUniqueID()
    {
        lastAssignedID += 1;
        return (float)lastAssignedID / totalObjects; // Normalize the ID
    }
}
Enter fullscreen mode Exit fullscreen mode

Identification

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Identification : MonoBehaviour
{
    private float _id;
    private Renderer _renderer;
    private MaterialPropertyBlock _propBlock;

    void Awake()
    {
        _id = IDManager.instance.GetUniqueID();
        _propBlock = new MaterialPropertyBlock();
        _renderer = this.gameObject.GetComponent<Renderer>();

        // TEMP
        Debug.Log("ID: " + _id);

        _renderer.GetPropertyBlock(_propBlock);
        _propBlock.SetFloat("_ID", _id);
        _renderer.SetPropertyBlock(_propBlock);
    }
}
Enter fullscreen mode Exit fullscreen mode

URP Shader

Shader "Custom/UniqueColor2"
{
    Properties
    {
        _ID ("ID", Range(0, 1)) = 0.01
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 100

        HLSLINCLUDE

        #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"

        CBUFFER_START(UnityPerMaterial)
            float _ID;
        CBUFFER_END

        struct VertexInput
        {
            float4 position : POSITION;
        };

        struct VertexOutput
        {
            float4 position : SV_POSITION;
        };

        ENDHLSL

        Pass
        {
            HLSLPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            VertexInput vert(VertexInput v)
            {
                VertexInput o;
                o.position = TransformObjectToHClip(v.position.xyz);
                return o;
            }

            float4 frag(VertexOutput i) : SV_Target
            {
                return float4(_ID, 0, 0, 1);
            }

            ENDHLSL
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Top comments (0)