public void Initialise(int numParticles, float waveParticleKillThreshold) { _particleContainerSize = numParticles; _waveParticleKillThreshold = waveParticleKillThreshold; _splatTexture = null; // TODO: write Compute shader to clear this buffer _waveParticlesBuffer = new ComputeBuffer((int)_particleContainerSize, WaveParticle.STRIDE); _gpuCommitParticles = Resources.Load <ComputeShader>(COMMIT_PARTICLES); _gpuSplatParticles = Resources.Load <ComputeShader>(SPLAT_PARTICLES); _gpuSubdivideParticles = Resources.Load <ComputeShader>(SUBDIVIDE_PARTICLES); kernel_CommmitParticles = _gpuCommitParticles.FindKernel(COMMIT_PARTICLES); kernel_SplatParticles = _gpuSplatParticles.FindKernel(SPLAT_PARTICLES); kernel_SubdivideParticles = _gpuSubdivideParticles.FindKernel(SUBDIVIDE_PARTICLES); kernel_AddSubdividedParticles = _gpuSubdivideParticles.FindKernel(ADD_SUBDIVIDED_PARTICLES); _particleEvents = new EventElement[_particleContainerSize]; for (int i = 0; i < _particleEvents.Length; i++) { _particleEvents[i] = EventElement.getDeadEvent(); } for (int i = 0; i < _subdivisionEvents.Length; i++) { _subdivisionEvents[i] = _NullParticleIndex; } _pendingParticles = new WaveParticle[numParticles / 10]; downsampleMaterial = new Material(Shader.Find("Unlit/DownSampleTexture")); DOWNSAMPLE_ANTI_ALIAS_FACTOR_ID = Shader.PropertyToID("_antiAliasFactor"); DOWNSAMPLE_TEXTURE_WIDTH_ID = Shader.PropertyToID("_textureWidth"); DOWNSAMPLE_TEXTURE_HEIGHT_ID = Shader.PropertyToID("_textureHeight"); }
/// <summary> /// Apply the subdivisions based on which particles need subdividing this frame. /// /// This happens in two Compute Shader steps: /// /// - The first takes all particles to be subdived and calculates the details of which particles will be created /// /// - The second takes all the newly created particles and adds them to the total pool of Wave Particles /// It also creates a buffer of data that can be used to calculate future subdivision events that will occur /// as a result of the new wave particles. /// /// </summary> /// <param name="currentFrame"></param> public void calculateSubdivisions(int currentFrame) { // Commit all pending particles that are stored in CPU memory to the GPU. commitParticles(); /// /// Count the number of subdivisions that will applied this frame. /// int numSubdivisions = 0; { int particleIndex = _subdivisionEvents[currentFrame]; while (particleIndex != _NullParticleIndex) { numSubdivisions++; particleIndex = _particleEvents[particleIndex].frontIndex; } } // If no subdivisions are required, return if (numSubdivisions == 0) { return; } /// /// Get the indices of all the particles that will be subdivided this frame. /// int[] particleIndicesToSubdivide = new int[numSubdivisions]; { int particleIndex = _subdivisionEvents[currentFrame]; for (int i = 0; i < particleIndicesToSubdivide.Length; i++) { particleIndicesToSubdivide[i] = particleIndex; particleIndex = _particleEvents[particleIndex].frontIndex; _particleEvents[particleIndicesToSubdivide[i]] = EventElement.getDeadEvent(); } _subdivisionEvents[currentFrame] = _NullParticleIndex; } /// /// Create buffers for storing particles to be subdivided and the resulting subdivisions. /// ComputeBuffer particleIndicesToSubdivideBuffer = new ComputeBuffer(numSubdivisions, sizeof(int)); particleIndicesToSubdivideBuffer.SetData(particleIndicesToSubdivide); // Create an append buffer for the newly created particles ComputeBuffer particlesToAddBuffer = new ComputeBuffer(numSubdivisions * 3, WaveParticle.STRIDE, ComputeBufferType.Append); particlesToAddBuffer.SetCounterValue(0); // Load particles to be subdivided into this buffer, and copy it out to get future subdivision details??? _gpuSubdivideParticles.SetInt(NUM_SUBDIVISIONS, numSubdivisions); _gpuSubdivideParticles.SetFloat(KILL_THRESHOLD, _waveParticleKillThreshold); _gpuSubdivideParticles.SetBuffer(kernel_SubdivideParticles, PARTICLE_INDICES_TO_SUBDIVIDE_BUFFER, particleIndicesToSubdivideBuffer); _gpuSubdivideParticles.SetBuffer(kernel_SubdivideParticles, WAVE_PARTICLE_BUFFER, _waveParticlesBuffer); _gpuSubdivideParticles.SetBuffer(kernel_SubdivideParticles, PARTICLES_TO_ADD_APPEND_BUFFER, particlesToAddBuffer); _gpuSubdivideParticles.Dispatch(kernel_SubdivideParticles, (numSubdivisions + THREAD_GROUPS_X) / THREAD_GROUPS_X, 1, 1); int numParticlesToAdd = particlesToAddBuffer.count; ComputeBuffer subdivisionDataBuffer = new ComputeBuffer(numParticlesToAdd, sizeof(int) * 2); _gpuSubdivideParticles.SetInt(CURRENT_HEAD, _currentHead); _gpuSubdivideParticles.SetInt(PARTICLE_CONTAINER_SIZE, _particleContainerSize); _gpuSubdivideParticles.SetInt(FRAME_CYCLE_LENGTH, WaveParticle.FRAME_CYCLE_LENGTH); _gpuSubdivideParticles.SetInt(NUM_PARTICLES_TO_ADD, numParticlesToAdd); _gpuSubdivideParticles.SetFloat(WAVE_PARTICLE_RADIUS, WaveParticle.RADIUS); _gpuSubdivideParticles.SetFloat(FIXED_DELTA_TIME, Time.fixedDeltaTime); _gpuSubdivideParticles.SetFloat(PARTICLE_SPEED, WaveParticle.PARTICLE_SPEED); _gpuSubdivideParticles.SetBuffer(kernel_AddSubdividedParticles, PARTICLES_TO_ADD_CONSUME_BUFFER, particlesToAddBuffer); _gpuSubdivideParticles.SetBuffer(kernel_AddSubdividedParticles, SUBDIVISION_DATA, subdivisionDataBuffer); _gpuSubdivideParticles.SetBuffer(kernel_AddSubdividedParticles, WAVE_PARTICLE_BUFFER, _waveParticlesBuffer); _gpuSubdivideParticles.Dispatch(kernel_AddSubdividedParticles, (numParticlesToAdd + THREAD_GROUPS_X) / THREAD_GROUPS_X, 1, 1); _currentHead = (_currentHead + numParticlesToAdd) % _particleContainerSize; int[,] subdivisionData = new int[numParticlesToAdd, 2]; subdivisionDataBuffer.GetData(subdivisionData); for (int i = 0; i < numParticlesToAdd; i++) { int particleIndex = subdivisionData[i, 0]; int subdivisionFrame = subdivisionData[i, 1]; addSubdivisionEvent(particleIndex, subdivisionFrame); } subdivisionDataBuffer.Release(); particlesToAddBuffer.Release(); particleIndicesToSubdivideBuffer.Release(); }