#pragma kernel main // The same particle data structure used by both the compute shader and the shader. struct Particle { float3 position; float3 velocity; }; // The buffer holding the particles shared with the regular shader. RWStructuredBuffer<Particle> particleBuffer; // parameters from GPU float deltaTime; // Even here deltaTime is needed! float3 targ1; // Targ1 position. float3 targ2; // Targ1 position. float targetStrengh; // Strengh, from the inspector! [numthreads(32,1,1)] // 32 is the minimal size to fullfill the wrap. this is just the number of thread to run by wrap, "x, y, z" make easy 3D indexing. void main (uint3 id : SV_DispatchThreadID) { // particleBuffer[id.x] is the particle this thread must Update, according to the thread ID. // Direction and distance to target. float3 dir1 = normalize((targ1 - particleBuffer[id.x].position)); float3 dir2 = normalize((targ2 - particleBuffer[id.x].position)); float dist1 = distance(targ1, particleBuffer[id.x].position); float dist2 = distance(targ2, particleBuffer[id.x].position); float3 dir; float dist; if(dist1 < dist2) { dist = dist1; dir = dir1; } else { dist = dist2; dir = dir2; } if(dist > 10) { particleBuffer[id.x].velocity += targetStrengh * dir * deltaTime / dist; } else { particleBuffer[id.x].velocity -= targetStrengh * dir * deltaTime / dist; } particleBuffer[id.x].velocity = particleBuffer[id.x].velocity* 0.98f; particleBuffer[id.x].position += particleBuffer[id.x].velocity * deltaTime; }
using UnityEngine; using System.Collections; using System.Collections.Generic; using UnityEngine.UI; public class TestParticle : MonoBehaviour { // The same particle data structure used by both the compute shader and the shader. struct Particle { public Vector3 position; public Vector3 velocity; }; public static List<TestParticle> list; // Static list, just to call Render() from the camera. public int warpCount = 10; // The number particle /32. public float initialSpread = 0.1f; // Initial speed to the center of the sphere. public float pPowerMag = 100; // velocity change from the mouse position. public Material material; // Use "particle" shader. public ComputeShader computeShader; // Use "moveParticle" compute shader. const int warpSize = 32; // GPUs process data by warp, 32 for every modern ones. int particleCount; // = warpSize * warpCount. ComputeBuffer particleBuffer; // The GPU buffer holding the particules. public GameObject frisbee1; public GameObject frisbee2; public UI_Control ui_Control; void Start () { // Just init the static list if (list == null) list = new List<TestParticle>(); list.Add(this); particleCount = warpCount * warpSize; // Init particles Particle[] particleArray = new Particle[particleCount]; for (int i = 0; i < particleCount; ++i) { particleArray[i].position = Random.insideUnitSphere * initialSpread; particleArray[i].velocity = particleArray[i].position.normalized; } // Instanciate and initialise the GPU buffer. particleBuffer = new ComputeBuffer(particleCount, 24); // 24 = sizeof(Particle) particleBuffer.SetData(particleArray); // bind the buffer to both the compute shader and the shader. computeShader.SetBuffer(0, "particleBuffer", particleBuffer); material.SetBuffer ("particleBuffer", particleBuffer); } void Update () { // Get the mouse position in the 3D space (a flat box collider catch the ray) . float[] targ1 = {0f, 0f, 0f }; float[] targ2 = { 0f, 0f, 0f }; Vector3 pos = frisbee1.transform.position; targ1[0] = pos.x; targ1[1] = pos.y; targ1[2] = pos.z; pos = frisbee2.transform.position; targ2[0] = pos.x; targ2[1] = pos.y; targ2[2] = pos.z; pPowerMag = ui_Control.pParticlePower; // Initialise the compute shader variables. computeShader.SetFloat("targetStrengh", pPowerMag); computeShader.SetFloat("deltaTime", Time.deltaTime); computeShader.SetFloats("targ1", targ1); computeShader.SetFloats("targ2", targ2); // Start the compute shader (move every particle for this frame). computeShader.Dispatch(0, warpCount, 1, 1); } // Called by the camera in OnRender public void Render () { // Bind the pass to the pipeline then call a draw (this use the buffer bound in Start() instead of a VBO). material.SetPass (0); Graphics.DrawProcedural (MeshTopology.Points, 1, particleCount); } void OnDestroy() { list.Remove(this); // Unity cry if the GPU buffer isn't manually cleaned particleBuffer.Release(); } }