← All work

Unreal Engine 5.8 plugin · C++ · HLSL · RDG · Metal / D3D12 / Vulkan

SlimeLab

A playable slime character. It's simulated on the GPU with MLS-MPM and drawn with its own rendering pass. Flatten it and it re-goos itself back together.

Particles
51,198
Simulation
8.2 ms
Rendering
1.2 ms
Measured on
M5 Pro · 1440p

Why

Asher Zhu's UE5 slime showed how much character a blob of goo can have. I wanted to build my own at the engine level, with physics that hold up up close and a look that reads as clear, wet and alive rather than as particles. The approach and code are my own. It's an engine plugin rather than a Niagara setup, hooked into the renderer through a scene view extension, and it runs natively on Apple Silicon.

P2G→Surface→Grid update→G2P× up to 64 substeps→Ray-march→Composite

Simulation

Scene collision

The slime collides with any mesh that has a distance field, with no authored colliders. The simulation runs in PostRenderBasePassDeferred, the earliest view-extension hook after the renderer updates the Global Distance Field for the frame. The grid update removes inward velocity with Coulomb friction, and particles that tunnel in are pushed out. It's a shader permutation, so platforms without distance fields fall back to the box. It costs about 0.6 ms.

Collision against scene meshes through the Global Distance Field.

A character, not a puddle

The body's shape is a mold: a signed distance field in the body frame. Goo outside it is pulled back in, and grid density control (after Thuerey et al. 2006) pushes goo from overfull cells into empty parts of the mold. The body frame comes from a deterministic two-pass GPU reduction (center of mass, velocity and rotation).

My first attempt gave each particle a rest position. It failed because mixing scrambles which particle belongs where, and the pulls cancel out. The mold doesn't care which particle goes where, so it re-goos however mixed things get.

Shape-shifting is a mold swap. Five volume-matched SDFs (blob, cube, donut, pill, star) blend into each other. Squash and lean come from springs driven by the body's velocity. The eyes ride whichever particle is nearest each socket, picked every frame with a packed (distance, index) atomic min, so they stay on the surface through any squash.

Morphing between molds. All shapes have the same volume, so nothing is gained or lost.
Crushed flat, then re-gooing.

Rendering

Performance

Packaged Development build, Apple M5 Pro (Metal SM6), 2560×1440, Lumen on, 51,198 particles, 64³ grid. Each number is the median of 10 ProfileGPU captures, because single captures on Metal varied more than 3×.

ScopeCollision onCollision off
Simulation (54 substeps)8.20 ms7.63 ms
Rendering1.21 ms0.91 ms

The substep count dominates cost. Simulated time per frame is capped at 1/60 s: substeps scale with simulated time, so without the cap a slow frame requests more substeps and the next frame is slower still.

Bugs worth telling

Texture atomics silently did nothing on Metal

Splatting was correct in logic but produced nothing. A debug view that tinted the whole screen proved the composite ran; one that wrote a fixed block of pixels, bypassing projection, proved the atomics weren't landing. Metal needs TexCreate_AtomicCompatible on any texture used with shader atomics. D3D12 doesn't.

The floor never pushed back

Particles were clamped 2.5 cells from the wall, but only cells 0–1 enforced the boundary, so no particle's 3×3×3 stencil ever touched a boundary cell. A GPU readback showed particle 0 pinned at the clamp with a velocity of −180 and still falling.

An unbound uniform buffer only the cook caught

The distance-field permutation includes engine helpers that read the View uniform buffer, which my parameter struct didn't declare. The editor ran it without complaint while reading garbage; the cook's shader-binding validation failed the build.

The shape mold turned into a rocket

Anchored at the body's lowest point, density control pushed the bottom layer upward, which moved the anchor up, which pushed again, and the slime flew. Anchoring on the lowest particle that is actually resting on something fixed it.

References