Created
December 21, 2025 12:25
-
-
Save adammyhre/81746e720b83c12c98829b22c3c7e7a8 to your computer and use it in GitHub Desktop.
Compute Shader Example
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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 | |
| } | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #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; | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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