Skip to content

Instantly share code, notes, and snippets.

Last active January 27, 2024 12:15
Show Gist options
  • Save Cyanilux/4046e7bf3725b8f64761bf6cf54a16eb to your computer and use it in GitHub Desktop.
Save Cyanilux/4046e7bf3725b8f64761bf6cf54a16eb to your computer and use it in GitHub Desktop.
Experiments with using DrawMeshInstancedIndirect with Shader Graph
// ----------------------------------------------------------------------------------
// Graph should contain Boolean Keyword, "PROCEDURAL_INSTANCING_ON", Global, Multi-Compile.
// Must have two Custom Functions in vertex stage. One is used to attach this file (see Instancing_float below),
// and another to set #pragma instancing_options :
// It must use the String mode as this cannot be defined in includes.
// Without this, you will get "UNITY_INSTANCING_PROCEDURAL_FUNC must be defined" Shader Error.
Out = In;
#pragma instancing_options procedural:vertInstancingSetup
// I've found this works fine, but it might make sense for the pragma to be defined outside of a function,
// so could also use this slightly hacky method too
Out = In;
#pragma instancing_options procedural:vertInstancingSetup
void dummy(){
// ----------------------------------------------------------------------------------
struct InstanceData {
float4x4 m;
StructuredBuffer<InstanceData> _PerInstanceData;
// Stores the matrices (and possibly other data) sent from the C# side via material.SetBuffer, in Start/OnEnable.
// See :
// Updates the unity_ObjectToWorld / unity_WorldToObject matrices so our matrix is taken into account
// Based on :
// and/or
void vertInstancingMatrices(inout float4x4 objectToWorld, out float4x4 worldToObject) {
InstanceData data = _PerInstanceData[unity_InstanceID];
objectToWorld = mul(objectToWorld, data.m);
// Transform matrix (override current)
// I prefer keeping positions relative to the bounds passed into DrawMeshInstancedIndirect so use the above instead
//objectToWorld._11_21_31_41 = float4(data.m._11_21_31, 0.0f);
//objectToWorld._12_22_32_42 = float4(data.m._12_22_32, 0.0f);
//objectToWorld._13_23_33_43 = float4(data.m._13_23_33, 0.0f);
//objectToWorld._14_24_34_44 = float4(data.m._14_24_34, 1.0f);
// Inverse transform matrix
float3x3 w2oRotation;
w2oRotation[0] = objectToWorld[1].yzx * objectToWorld[2].zxy - objectToWorld[1].zxy * objectToWorld[2].yzx;
w2oRotation[1] = objectToWorld[0].zxy * objectToWorld[2].yzx - objectToWorld[0].yzx * objectToWorld[2].zxy;
w2oRotation[2] = objectToWorld[0].yzx * objectToWorld[1].zxy - objectToWorld[0].zxy * objectToWorld[1].yzx;
float det = dot(objectToWorld[0].xyz, w2oRotation[0]);
w2oRotation = transpose(w2oRotation);
w2oRotation *= rcp(det);
float3 w2oPosition = mul(w2oRotation, -objectToWorld._14_24_34);
worldToObject._11_21_31_41 = float4(w2oRotation._11_21_31, 0.0f);
worldToObject._12_22_32_42 = float4(w2oRotation._12_22_32, 0.0f);
worldToObject._13_23_33_43 = float4(w2oRotation._13_23_33, 0.0f);
worldToObject._14_24_34_44 = float4(w2oPosition, 1.0f);
void vertInstancingSetup() {
vertInstancingMatrices(unity_ObjectToWorld, unity_WorldToObject);
// Shader Graph Functions
// Obtain InstanceID. e.g. Can be used as a Seed into Random Range node to generate random data per instance
void GetInstanceID_float(out float Out){
Out = 0;
Out = unity_InstanceID;
// Just passes the position through, allows us to actually attach this file to the graph.
// Should be placed somewhere in the vertex stage, e.g. right before connecting the object space position.
void Instancing_float(float3 Position, out float3 Out){
Out = Position;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment