[Unity] GameObject to Shader Communication: MaterialPropertyBlocks
Published: 2023-06-18 17:56:01

## 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

```cs
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
    }
}
```

## Identification

```cs
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);
    }
}
```

## 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
        }
    }
}
```