Sample
I first started with the given sample since it was a decent starting point with physics and attractor logic implemented. While repeating the sample i tweaked some parts. A lot of them were trivial changes to accommodate incoming specifications, I want to mention one. Instead of using imageBuffers i replaced them with Shader Storage Buffers to reduce the boilerplate and write a more intuitive code as the OpenGL wiki mentions “Functionally speaking, SSBOs can be thought of as a much nicer interface to Buffer Textures when accessed via Image Load Store.”
Physics Implementation
The compute shader handles all particle updates in parallel. Each particle has position, velocity, and age stored in vec4 format – position in xyz and age in the w component. The gravitational physics uses a simple formula:
vec3 dir = attractors[i].xyz - pos.xyz;
float dist_sq = dot(dir, dir) + 10.0;
vel.xyz += dt * G * attractors[i].w * normalize(dir) / dist_sq;
The +10.0 prevents particles from accelerating infinitely when they get too close to attractors. When particles die (age reaches 0), they respawn at the origin with random velocity using hash functions for circular distribution.
Interactive Attractor System
With multiple options listed below attractor system created an immersive experience:
- Switch between “Origin Mode” and “Attractor Mode” using the ‘G’ key
- Left-click to place new attractors (up to 12 maximum)
- Right-click to remove attractors
- Scroll wheel to adjust attractor mass (10-100 units)
The mouse coordinate conversion to world space was tricky with different aspect ratios. Had to properly convert screen coordinates to NDC then scale by the orthographic projection bounds.
Visual Design Choices
The particle rendering uses a color-coded intensity system that transitions from green (newborn) through yellow to red (aged), creating visually appealing trails that indicate particle age. Additive blending (GL_ONE, GL_ONE) creates bright glowing effects where particles overlap.
Text rendering shows current attractor count, mass value, and mode. There’s also a zen mode toggle to hide all UI for clean visuals.
Performance Remarks
While I didn’t rigorously tested performance. I believe up to 10.000.000 particles with size 1 are working visually satisfying. The compute shader dispatches work groups of 128 threads which scales well with particle count. GPU memory bandwidth becomes the main bottleneck rather than computation.
Difficulties and Solutions
The main challenge was getting the coordinate systems right – converting from screen space to world space while maintaining correct aspect ratios in fullscreen mode. Also had to be careful with memory barriers between compute dispatch and rendering to avoid race conditions.
Buffer management was straightforward with SSBOs compared to texture buffers. The ping-pong approach with two buffers for positions and velocities worked cleanly.
Results
The final result creates chaotic patterns that respond to user interaction. Different attractor configurations produce flow patterns.