Skip to content

Instantly share code, notes, and snippets.

@adammyhre
Created December 21, 2025 12:25
Show Gist options
  • Select an option

  • Save adammyhre/81746e720b83c12c98829b22c3c7e7a8 to your computer and use it in GitHub Desktop.

Select an option

Save adammyhre/81746e720b83c12c98829b22c3c7e7a8 to your computer and use it in GitHub Desktop.
Compute Shader Example
Shader "Crowd/InstancedAgent" {
SubShader {
Tags { "RenderPipeline"="UniversalRenderPipeline" "RenderType"="Opaque" }
Pass {
HLSLPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
struct CrowdAgent {
float2 position;
float2 velocity;
float maxSpeed;
float speed01;
};
StructuredBuffer<CrowdAgent> agents;
struct Attributes {
float3 positionOS : POSITION;
uint instanceID : SV_InstanceID;
};
struct Varyings {
float4 positionCS : SV_POSITION;
float speed01 : TEXCOORD0;
};
Varyings vert(Attributes v) {
CrowdAgent agent = agents[v.instanceID];
float3 positionOS = v.positionOS;
positionOS.x += agent.position.x;
positionOS.z += agent.position.y;
Varyings o;
o.positionCS = TransformObjectToHClip(positionOS);
o.speed01 = agent.speed01;
return o;
}
half4 frag(Varyings i) : SV_Target {
half3 slowColor = half3(0.2, 0.4, 1.0);
half3 fastColor = half3(1.0, 0.2, 0.2);
half3 color = lerp(slowColor, fastColor, i.speed01);
return half4(color, 1);
}
ENDHLSL
}
}
}
#pragma kernel MoveAgents
struct CrowdAgent {
float2 position;
float2 velocity;
float maxSpeed;
float speed01;
};
RWStructuredBuffer<CrowdAgent> agents;
int agentCount;
float deltaTime;
float2 targetPosition;
[numthreads(256, 1, 1)]
void MoveAgents(uint3 id : SV_DispatchThreadID) {
uint index = id.x;
if (index >= agentCount) return;
CrowdAgent agent = agents[index];
float2 toTarget = targetPosition - agent.position;
float distance = length(toTarget);
if (distance > 0.1) {
float2 desired = normalize(toTarget) * agent.maxSpeed;
agent.velocity = lerp(agent.velocity, desired, 0.05);
} else {
agent.velocity = float2(0.0, 0.0);
}
float speed = length(agent.velocity);
float targetSpeed01 = saturate(speed / agent.maxSpeed);
agent.speed01 = targetSpeed01;
agent.position += agent.velocity * deltaTime;
agents[index] = agent;
}
using System.Runtime.InteropServices;
using UnityEngine;
public class CrowdSimulationController : MonoBehaviour {
#region Fields
[Header("Target")]
[SerializeField] Transform target;
[Header("Simulation")]
[SerializeField] int agentCount = 10000;
[SerializeField] float worldSize = 50f;
[Header("Rendering")]
[SerializeField] Mesh agentMesh;
[SerializeField] Material agentMaterial;
[Header("Compute")]
[SerializeField] ComputeShader crowdShader;
ComputeBuffer agentBuffer;
int moveKernel;
Bounds drawBounds;
const string MoveKernelName = "MoveAgents";
static readonly int TargetPositionID = Shader.PropertyToID("targetPosition");
static readonly int AgentCountID = Shader.PropertyToID("agentCount");
static readonly int DeltaTimeID = Shader.PropertyToID("deltaTime");
static readonly int AgentsID = Shader.PropertyToID("agents");
#endregion
void Start() {
InitializeAgents();
InitializeCompute();
InitializeRendering();
}
void Update() {
var pos = target.position;
crowdShader.SetVector(TargetPositionID, new Vector4(pos.x, pos.z, 0, 0));
crowdShader.SetFloat(DeltaTimeID, Time.deltaTime);
DispatchSimulation();
DrawAgents();
}
void OnDestroy() => agentBuffer?.Release();
void DrawAgents() {
Graphics.DrawMeshInstancedProcedural(
agentMesh,
0,
agentMaterial,
drawBounds,
agentCount
);
}
void DispatchSimulation() {
var threadGroups = Mathf.CeilToInt(agentCount / 256f);
crowdShader.Dispatch(moveKernel, threadGroups, 1, 1);
}
void InitializeRendering() {
agentMaterial.SetBuffer(AgentsID, agentBuffer);
drawBounds = new Bounds(Vector3.zero, Vector3.one * worldSize * 2f);
}
void InitializeCompute() {
moveKernel = crowdShader.FindKernel(MoveKernelName);
crowdShader.SetInt(AgentCountID, agentCount);
crowdShader.SetBuffer(moveKernel, AgentsID, agentBuffer);
}
void InitializeAgents() {
// agentBuffer = new ComputeBuffer(agentCount, sizeof(float) * 7);
agentBuffer = new ComputeBuffer(agentCount, Marshal.SizeOf<CrowdAgent>());
var agents = new CrowdAgent[agentCount];
for (var i = 0; i < agentCount; i++) {
agents[i] = new CrowdAgent {
position = Random.insideUnitCircle * worldSize,
velocity = Vector2.zero,
maxSpeed = Random.Range(2f, 4f)
};
}
agentBuffer.SetData(agents);
}
}
[StructLayout(LayoutKind.Sequential)]
public struct CrowdAgent {
public Vector2 position;
public Vector2 velocity;
public float maxSpeed;
public float speed01;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment