Skip to content

Latest commit

 

History

12 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

nova

A real-time GPU particle simulator in C++20 and raw Vulkan. A million-plus particles are simulated and rendered entirely on the GPU, orbiting gravity wells you place with the mouse, and the whole field reacts live to whatever audio your system is playing.

nova running in gravity mode, reacting to music

Build

Dependencies: a Vulkan driver, glslangValidator, GLFW, and GLM. On Fedora:

sudo dnf install cmake gcc-c++ vulkan-loader-devel glslang glfw-devel glm-devel

On Debian/Ubuntu the equivalents are libvulkan-dev glslang-tools libglfw3-dev libglm-dev.

cmake -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build -j$(nproc)
./build/nova

Shaders in shaders/ are compiled to SPIR-V by the build (glslangValidator, wired up as custom commands in CMake), nothing precompiled is checked in. Dear ImGui and miniaudio are vendored in third_party/.

Controls

  • Left click: place a gravity well, or grab and drag a nearby one
  • Right click: remove the nearest well
  • Tab: switch between gravity and boids mode
  • Panel: particle count, gravity strength, well strength, boid rule weights, live audio band meters, reset

Audio capture prefers a monitor/loopback source (PipeWire and PulseAudio expose these), so it reacts to whatever the system is playing. If no monitor source exists it falls back to the default capture device. Band levels are normalized by a slow per-band auto gain, so it works at any system volume.

Performance

Measured on an RTX 4060 Laptop GPU (driver 580 series, Vulkan 1.4), 1280x720, release build. GPU times are from Vulkan timestamp queries around the compute and render passes; fps is the uncapped wall-clock rate with mailbox present.

Gravity mode:

Particles fps compute render
500k 1048 0.030 ms 0.554 ms
1M 641 0.062 ms 0.995 ms
2M 371 0.230 ms 1.86 ms
4M 205 0.518 ms 3.68 ms

Boids mode (spatial grid neighbor search, 64x64 cells):

Particles fps compute render
500k 143 6.63 ms 0.225 ms
1M 75 12.5 ms 0.340 ms

Render covers three passes: the trail fade plus particle accumulation into a half-float offscreen target, the tonemapped composite, and a second particle draw for the live layer. At 1M particles gravity mode uses about 1 ms of GPU time per frame, roughly 6% of a 60fps budget. Boids is the expensive mode because every particle surveys its 3x3 grid neighborhood, and it still clears 60fps at 1M.

The CPU does no per-particle work. Per frame it updates push constants (audio bands, delta time), writes at most 16 gravity wells with vkCmdUpdateBuffer, and records the dispatches and one draw. Particle state lives in two device-local ping-pong buffers, one read and one written per dispatch, with a single pipeline barrier covering both the vertex-input read in the same frame and the compute read in the next.

What is implemented

  • Vulkan context, swapchain, compute and graphics pipelines by hand, no wrapper libraries
  • N-body gravity toward user-placed wells with softened falloff, orbital seeding, framerate-independent drag, and a gentle curl force tuned against the drag so coherent rotation survives and winds into spiral arms while random motion decays
  • Boids (separation, alignment, cohesion) over the same particle buffer, using a fixed-capacity uniform grid rebuilt on the GPU each frame
  • Audio capture through miniaudio, 1024-point FFT, bass/mid/treble bands with per-band auto gain; bass punches well strength and boid cohesion, mid stirs the swirl and swells point size, treble adds shimmer and brightness
  • Persistence trails: particles accumulate into a half-float HDR buffer that fades instead of clearing, then a fullscreen pass tonemaps it (Reinhard plus a black floor and vignette), with the live positions drawn crisp on top
  • GPU timestamp profiling (the numbers above), Dear ImGui panel, frame dump mode (NOVA_DUMP=dir) for capturing footage

Not implemented: true all-pairs N-body (the attractors are the wells, all-pairs is O(N^2) and dies orders of magnitude before 1M), 3D simulation, a separate bloom blur chain. The glow comes from additive accumulation and the tonemap.

Debug knobs, all environment variables: NOVA_PARTICLES=n initial count, NOVA_BOIDS=1 start in boids mode, NOVA_PROFILE=1 per-second timing lines on stderr, NOVA_VALIDATION=1 enable validation layers, NOVA_SELFTEST=1 run a compute readback self-test at startup, NOVA_DUMP=dir write frames as PPM.

Credits

About

Real-time GPU compute particle simulator in C++20 and Vulkan. Millions of particles, N-body gravity and boids, audio reactive.

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages