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)