#version 450 #extension GL_ARB_separate_shader_objects : enable #extension GL_ARB_shading_language_420pack : enable struct Particle { vec2 pos; vec2 vel; vec4 gradientPos; }; // Binding 0 : Position storage buffer layout(std140, binding = 0) buffer Pos { Particle particles[ ]; }; layout (local_size_x = 256) in; layout (binding = 1) uniform UBO { float deltaT; float destX; float destY; int particleCount; } ubo; vec2 attraction(vec2 pos, vec2 attractPos) { vec2 delta = attractPos - pos; const float damp = 0.5; float dDampedDot = dot(delta, delta) + damp; float invDist = 1.0f / sqrt(dDampedDot); float invDistCubed = invDist*invDist*invDist; return delta * invDistCubed * 0.0035; } vec2 repulsion(vec2 pos, vec2 attractPos) { vec2 delta = attractPos - pos; float targetDistance = sqrt(dot(delta, delta)); return delta * (1.0 / (targetDistance * targetDistance * targetDistance)) * -0.000035; } void main() { // Current SSBO index uint index = gl_GlobalInvocationID.x; // Don't try to write beyond particle count if (index >= ubo.particleCount) return; // Read position and velocity vec2 vVel = particles[index].vel.xy; vec2 vPos = particles[index].pos.xy; vec2 destPos = vec2(ubo.destX, ubo.destY); vec2 delta = destPos - vPos; float targetDistance = sqrt(dot(delta, delta)); vVel += repulsion(vPos, destPos.xy) * 0.05; // Move by velocity vPos += vVel * ubo.deltaT; // collide with boundary if ((vPos.x < -1.0) || (vPos.x > 1.0) || (vPos.y < -1.0) || (vPos.y > 1.0)) vVel = (-vVel * 0.1) + attraction(vPos, destPos) * 12; else particles[index].pos.xy = vPos; // Write back particles[index].vel.xy = vVel; particles[index].gradientPos.x += 0.02 * ubo.deltaT; if (particles[index].gradientPos.x > 1.0) particles[index].gradientPos.x -= 1.0; }