#version 450 layout(local_size_x = 128) in; struct EmitterParameters { vec4 angularRangesAndOffsets; float minSpeed; float maxSpeed; float padding[2]; vec4 lifeAndSizeRange; // min life, max life, min size, max size }; struct ParticleData { vec4 position; vec4 rotation; vec4 velocity; vec4 acceleration; vec4 scaleAndDecay; float life; uint textureIndex; }; struct GenericData { //! Delta time float dt; //! Elapsed time of the application float elapsedTime; //! Viewport width of the scene (excluding imgui, that means smaller than window) uint viewportWidth; //! Ditto but for height uint viewportHeight; }; layout (set = 0, binding = 0) uniform GenericDataBuffer { GenericData data; } genericDataBuffer; layout (std430, set = 2, binding = 0) readonly buffer EmitterBuffer { EmitterParameters data; } emitterParams; layout (std430, set = 2, binding = 1) coherent restrict buffer ParticlesInputBuffer { ParticleData data[]; } inputParticles; // output buffer not needed // layout (std430, set = 2, binding = 2) coherent restrict buffer ParticlesOutputBuffer // { // ParticleData data[]; // } outputParticles; layout (std430, set = 2, binding = 3) coherent restrict buffer ParticlesFreelistBuffer { int freeCount; int freeIndices[]; } freelist; // push constants layout(std140, push_constant) uniform EmitterPushConstant { vec4 emitterPosition; uint emissionCount; } emitterPushConstant; uint pcg_hash(uint seed) { uint state = seed * 747796405u + 2891336453u; uint word = ((state >> ((state >> 28u) + 4u)) ^ state) * 277803737u; return (word >> 22u) ^ word; } // Used to advance the PCG state. uint rand_pcg(inout uint rng_state) { uint state = rng_state; rng_state = rng_state * 747796405u + 2891336453u; uint word = ((state >> ((state >> 28u) + 4u)) ^ state) * 277803737u; return (word >> 22u) ^ word; } // Advances the prng state and returns the corresponding random float. float rand(inout uint state) { uint x = rand_pcg(state); state = x; return float(x)*uintBitsToFloat(0x2f800004u); } float map(float value, float inMin, float inMax, float outMin, float outMax) { return outMin + (outMax - outMin) * (value - inMin) / (inMax - inMin); } void main() { uint emitterInvocationIndex = gl_GlobalInvocationID.x; vec4 emitterPosition = emitterPushConstant.emitterPosition; vec4 angularRangesAndOffsets = emitterParams.data.angularRangesAndOffsets; float minSpeed = emitterParams.data.minSpeed; float maxSpeed = emitterParams.data.maxSpeed; if (emitterInvocationIndex >= emitterPushConstant.emissionCount) return; // Freecount will start at max particles. Here we subtract every time we emit. int freelistIndex = atomicAdd (freelist.freeCount, -1) - 1; if (freelistIndex < 0) atomicAdd (freelist.freeCount, 1); ParticleData particle; // Get seed for randomization uint pixel_index = uint (emitterPosition.x + emitterPosition.y + floatBitsToUint(genericDataBuffer.data.elapsedTime) * (gl_GlobalInvocationID.x + 1)); uint seed = pcg_hash (pixel_index); int index = freelist.freeIndices[freelistIndex]; // emit particle from emitter position particle.position = vec4 (emitterPosition.xyz, 1.0f); vec2 eulerAngles = vec2 (rand(seed) * angularRangesAndOffsets.x + angularRangesAndOffsets.z, rand(seed) * angularRangesAndOffsets.y + angularRangesAndOffsets.w); // Set its velocity particle.velocity.xyz = vec3 (cos(eulerAngles.x) * cos(eulerAngles.y), sin(eulerAngles.x) * cos(eulerAngles.y), sin(eulerAngles.y)); particle.velocity *= map (rand (seed), 0.0f, 1.0f, minSpeed.x, maxSpeed.x); // randomize life value that ranges from minLife to maxLife particle.life = map (rand(seed), 0.0f, 1.0f, emitterParams.data.lifeAndSizeRange.x, emitterParams.data.lifeAndSizeRange.y); float particleSize = map (rand(seed), 0.0f, 1.0f, emitterParams.data.lifeAndSizeRange.z, emitterParams.data.lifeAndSizeRange.w); // Set size of particle particle.scaleAndDecay.x = particleSize; particle.scaleAndDecay.z = particleSize; particle.rotation = vec4 (5.0f); particle.acceleration = vec4 (0.01f); inputParticles.data[index] = particle; }