GPU particle shatter
One rendered frame of a room becomes 115,200 GPU particles.
Left: the hit throws them through the floor.
Right: a voxel grid built from the particles stops them, and they pile up.
The problem
Breaking a whole scene apart usually needs a fracture model for every object. A cheaper trick turns what the camera sees into points and lets a compute shader move them. Topic via @ValigurskyM, who did this on @playcanvas. I built my own room and pipeline on the PlayCanvas engine with WebGPU compute.
How it works
- The camera renders the room once and keeps its depth and colour maps.
- A compute shader runs one thread per 2x2 pixel block. It reads depth, rebuilds the world position, copies the colour, and writes a 48-byte particle. 640x720 gives 115,200 particles; 1280x720 gives 230,400.
- The meshes are switched off. From then on the room on screen is only particles, drawn as camera-facing quads sized to cover their pixels.
- On the hit, a shock front grows at 4.5 m/s from the impact point, capped at 1.6 m. Each particle it passes gets a push that fades with distance, plus jitter and a fixed push out of the wall.
- Every 60 Hz step runs three compute passes: clear a voxel grid of 4 cm cells, count the particles still at rest into it, then move the flying ones with gravity (9.81 m/s²) and drag.
- A flying particle that enters a cell holding 2 or more resting particles bounces. Slow enough with a full cell below, it settles and becomes part of the grid itself, so debris stacks on debris.
What I hit
My first shock front had no cap, so it brought down the whole room. The radius limit keeps it to a hole in one wall.
With a cell cap of 6 the debris spread into a thin carpet. At 2 it forms piles.
My side-view shots first captured from their own camera. The capture must always come from the main camera, or the orbit shows a different cloud.
I tried to time the compute passes, but the engine submits them at frame end, so my numbers measured nothing. There are no timings here.
My build
A procedural room: floor boards, bookshelf, table, chairs, sofa, lamp, plant. Both sides of the split use the same capture and the same hit at 1.2 s. The only difference is the collision flag. The slow-motion shot is the same scene re-run with 180 Hz steps.
Trade-offs
- Any scene the camera can see becomes debris, with no per-object setup
- One grid handles floor, furniture and piles
- Only surfaces facing the camera exist. From the side the room is a shell
- Lighting and shadows are baked into the particle colours
- Flying particles don't collide with each other, only with resting ones
- The grid is a fixed box (6.8 x 3.6 x 6 m, 9 MB)
When to use it
Scene transitions, "the world breaks" moments and reveal effects, where the camera stays near the capture angle.
Source and credits
Topic credit: @ValigurskyM (scene-to-particles demo on PlayCanvas, @playcanvas).
Engine: PlayCanvas engine by @playcanvas, https://github.com/playcanvas/engine, MIT license, branch main at commit 467e2db2f2e0de20bc8211b23e865115828d515b (the npm build playcanvas 2.24.0-beta.31 matches it). Used through its public API: WebGPU graphics device, Compute, ComputePass, StorageBuffer, the scene depth and colour maps for compute (sceneDepthCS / sceneColorCS), and ShaderMaterial with WGSL. The compute particle and scene-map examples in the engine repo were read for how these fit together.
Code: the engine is used unchanged; no source files were copied or modified. The capture shader, shock front, voxel occupancy grid, collision and settling rules, particle quad shader, procedural room, camera moves and compositing are my own. Everything in the video was rendered by me; no footage, screenshots or scene from the original post, and no Gaussian splats. No external assets. No IA model.