RenderMeshInstanced doesn't produce unique InstanceIDs in URP

I’m trying to render large numbers of objects using Graphics.RenderMeshInstanced(), with the plan to pass some per-instance unique data in a buffer that I read from on the GPU, using the InstanceID of each instance as an index.

However, it seems like InstanceIDs are not unique. I’ve created a clean project with a very simple script that renders a couple hundred instances and a shader that renders the object blue if its index is == 1 and red if not. Every 454 objects, the indeces seem to start again from 0.

This is the script I’m using for rendering:

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

public class Renderer : MonoBehaviour
{
    [SerializeField] public Material material;
    [SerializeField] public Mesh mesh;
    [SerializeField] float spacing = 0.1f;
    [SerializeField] int rows = 10;
    [SerializeField] int columns = 10;
    [SerializeField] float size = 0.1f;

    List<VegetationInstanceData> instData;
    RenderParams rp;

    [System.Serializable]
    public struct VegetationInstanceData
    {
        public Matrix4x4 objectToWorld;
    }

    // Start is called before the first frame update
    void Start()
    {
        instData = new List<VegetationInstanceData>();
        rp = new RenderParams(material);

        for(int row = 0; row < rows; row++)
        {
            for (int column = 0; column < columns; column++)
            {
                Vector3 worldPos = new Vector3(row * spacing, 0f, column * spacing);
                Matrix4x4 matrix = Matrix4x4.TRS(worldPos, Quaternion.Euler(90f, 0f, 0f), Vector3.one * size);
                instData.Add(new VegetationInstanceData() { objectToWorld = matrix});
            }
        }
    }

    // Update is called once per frame
    void Update()
    {
        if (instData != null && instData.Count > 0)
            Graphics.RenderMeshInstanced(rp, mesh, 0, instData);
    }
}

This is the shader graph of the material that’s being passed to the RenderParams (the “Instances” variable is a float, set to 1):

And this is what I’m seeing in the Game View:

I feel like I’m either doing something wrong here or I’ve fundamentally misunderstood how InstanceIDs are meant to work. After a lot of reading up on it I can’t seem to find an answer and would be super grateful if someone has some advice!

This is on Mac, Unity 2022.3. Silicon, but I’ve encountered the same issue in Unity 6.0 preview.

Thanks <3

Gist is the instance ID provided is just an array index or something similar for internal batching based on the device’s capability, not necessarily meant to be unique within a call, and 454 happens to be the computed instance limit for you.

1 Like

Oh right! Thanks for the clarification, I didn’t find this info anywhere.

A lot of examples of RenderMeshInstanced out there use the instance ID to access additional per instance data passed to Shader Graph, as if the IDs were unique.

If this is not the case, is there any way to pass additional per instance data to a Shader Graph shader and use it for rendering when using RenderMeshInstanced? Can we somehow access StructuredBuffers or ComputeBuffers?

For lack of a standard way to go about doing this surfaced in the shader graph UI, one would utilize custom function nodes, where you end up writing the buffer declaration and an accessor function in the function node.

1 Like

Thanks again! That makes sense, but which index / ID would you use in that accessor function to get the right element from the buffer?

I thought that’s what Instance_ID was for, but since they aren’t unique they won’t work for that.