I am working on a GPU particle system in a VRChat world. My plan is to put the position data into a texture and then use a vertex shader to position the particles – this way the simulation will happen completely on the GPU.
I instance the particles using DrawMeshInstanced:
VRCGraphics.DrawMeshInstanced(
particleMesh,
0, // submesh index
instanceMaterial,
instanceMatrices,
instanceCount,
propertyBlock,
UnityEngine.Rendering.ShadowCastingMode.Off,
false, // receive shadows
layer
);
The transform matrices are set to a random position and the vertex shader removes this before applying its own transform. This vertex shader positioning works correctly until I try to access each instance with a unique ID.
This code works. I have commented out the stuff that relies on the instance ID. When it runs I see a single particle moving up and down with a sin wave (this is in fact 1023 particles with the same position):
Shader "Unlit/ParticleRenderShader"
{
Properties {
_MainTex ("Position Texture", 2D) = "white" {}
[HDR] _Color ("Color", Color) = (1,1,1,1)
_ParticleSize ("Size", Float) = 0.1
}
SubShader {
// Important tags for transparency and instancing support
Tags { "RenderType"="Transparent" "Queue"="Transparent" "IgnoreProjector"="True" }
Blend SrcAlpha OneMinusSrcAlpha // Standard transparency blend
ZWrite Off // Usually off for transparent particles
Cull Off // Draw both sides
Pass {
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_instancing
#pragma instancing_options procedural:setup
#include "UnityCG.cginc"
sampler2D _MainTex;
float4 _MainTex_TexelSize;
float4 _Color;
//sampler2D _PositionTex;
//float4 _PositionTex_TexelSize;
//float _ParticleSize;
struct appdata {
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
//uint instanceID : SV_InstanceID;
UNITY_VERTEX_INPUT_INSTANCE_ID
};
struct v2f {
float2 uv : TEXCOORD0;
float4 vertex : SV_POSITION;
UNITY_VERTEX_OUTPUT_STEREO
};
v2f vert (appdata v) {
v2f o;
UNITY_SETUP_INSTANCE_ID(v);
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
// Get the instance ID
//uint id = v.instanceID;
// // Convert 1D ID to 2D UV coordinates for the CRT texture
// float width = _MainTex_TexelSize.z; // Texture width in pixels
// uint2 pixelCoords = uint2(id % (uint)width, id / (uint)width);
// // // Offset to center of pixel for accurate sampling
// float2 uv = (float2(pixelCoords) + 0.5) * _MainTex_TexelSize.xy;
// // Sample the position CRT
// float4 posData = tex2Dlod(_MainTex, float4(uv, 0, 0));
// Transform vertex to world space (maintains mesh shape)
float4 worldPos = mul(unity_ObjectToWorld, v.vertex);
// Reset object's world position to zero (remove transform position)
worldPos.xyz -= unity_ObjectToWorld._m03_m13_m23;
// Add offset position
//worldPos.xyz += posData.xyz * 0.01;
worldPos.y += sin(_Time.y) * 3.0;
// Transform to clip space
o.vertex = mul(UNITY_MATRIX_VP, worldPos);
o.uv = v.uv;
return o;
}
fixed4 frag (v2f i) : SV_Target {
return fixed4(1.0, 1.0, 1.0, 1.0);
}
ENDCG
}
}
}
As soon as I uncomment the SV_InstanceID here the code stops working and no particles can be seen - no error in console - tested both in editor and build and test on windows:
struct appdata {
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
//uint instanceID : SV_InstanceID;
UNITY_VERTEX_INPUT_INSTANCE_ID
};
Is SV_InstanceID unsupported by VRChat or is there some extra code I would need to include to make it work?
I have also tried:
uint id = unity_InstanceID;
But that results in a compiler error.
What I need is a unique ID for each instance that I can access in the shader. Is this possible in VRChat?
Btw the positions will be calculated in a separate shader outputting to a custom render texture - the above shader is used just for rendering the particles.
Using Unity 2022.3.22f1 with an nvidia GPU
