Skip to content

Instantly share code, notes, and snippets.

@EtiTheSpirit
Last active December 30, 2023 12:36
Show Gist options
  • Save EtiTheSpirit/78b7f57da19323f9f9d8a392369b37f0 to your computer and use it in GitHub Desktop.
Save EtiTheSpirit/78b7f57da19323f9f9d8a392369b37f0 to your computer and use it in GitHub Desktop.
Setting up instanced shaders

Upgrading Unlit Shaders to SPSI Rendering

Step 1: Under CGPROGRAM, place these compiler directives:

#pragma multi_compile_instancing

// IFF the scale of the model is the same for all axes (x, y, and z are ALWAYS the same number), put this next line.
// If not, do NOT use this next line. This is an optimization for the instancer, but it requires a uniform scale.
#pragma instancing_options assumeuniformscaling

Step 2: Add this to struct appdata after the last data line (before the } that ends it) - NO SEMICOLON AT THE END!

UNITY_VERTEX_INPUT_INSTANCE_ID

// For example:
struct appdata {
	float4 vertex : POSITION;
	float2 uv : TEXCOORD0;
	UNITY_VERTEX_INPUT_INSTANCE_ID
}

Step 3: Add these to struct v2f after the last data line (before the } that ends it) - NO SEMICOLON AT THE END!

UNITY_VERTEX_OUTPUT_STEREO
UNITY_VERTEX_INPUT_INSTANCE_ID  (iff you need to access instanced properties in fragment per-pass)

// For example:
struct v2f {
	float4 vertex : SV_POSITION;
	float2 uv : TEXCOORD0;
	UNITY_VERTEX_OUTPUT_STEREO
	// UNITY_VERTEX_INPUT_INSTANCE_ID If you need instanced data in the fragment program
}

Step 4: At the top of v2f vert(...), after the line that says v2f o;

UNITY_SETUP_INSTANCE_ID(v);
UNITY_INITIALIZE_OUTPUT(v2f, o);
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);

// For example:
v2f vert(appdata v) {
	v2f o;
	UNITY_SETUP_INSTANCE_ID(v);
	UNITY_INITIALIZE_OUTPUT(v2f, o);
	UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
	
	o.vertex = UnityObjectToClipPos(v.vertex);
	// etc. etc. ...
}

Step 5: Add this to frag, on the first line, IFF you need to access instanced data in the fragment program:

UNITY_SETUP_INSTANCE_ID(i);

// For example:
half4 frag(v2f i) {
	UNITY_SETUP_INSTANCE_ID(i);
	// etc. etc. ...
}

Step 6: (Optional) Translate all properties to instanced properties:

This is optional in that it is not required to make SPSI work. From Unity,

"You don’t always need to define per-instance properties. However, setting up an instance ID is mandatory, because world matrices need it to function correctly."

("Props" can be changed to anything, so long as it matches in all lines that need it, seen below) Note the lack of semicolons - this is intentional!

Replace all variable declarations, excluding uniforms and samplers, with buffer declarations. For example:

float _MyParameter;
float4 _MyVectorParameter;
uniform float _SomeExternalValue;
sampler2D _MainTex;

Will become:

UNITY_INSTANCING_BUFFER_START(Props)
	UNITY_DEFINE_INSTANCED_PROP(float, _MyParameter)
	UNITY_DEFINE_INSTANCED_PROP(float4, _MyVectorParameter)
UNITY_INSTANCING_BUFFER_END(Props)

sampler2D _MainTex; // Samplers of any type are not supported as instanced properties. Do not use them in DEFINE_INSTANCED_PROP.
uniform float _SomeExternalValue; // Uniforms are not supported as instanced properties either (they are global, so this would make little sense anyway)

Then, when using these variables (reading from them) replace them with:

float myParameter = UNITY_ACCESS_INSTANCED_PROP(Props, _MyParameter); // Store as a variable for convenience.
// You can also set the value this way, though there is often little reason to do this:
UNITY_ACCESS_INSTANCED_PROP(Props, _MyParameter) = 12345;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment