Fog is one of those things players only notice when it goes wrong. Ours went wrong in two ways: it looked like a flat grey wall, and it didn't care at all about the player walking through it.
The problem
The first version used a single volume around the whole area. It was cheap, but it never moved. When you walked forward, the fog stayed exactly where it was, so the world felt like a painting instead of a place.
Following the player, but not too closely
The new system spawns fog particles around the player and keeps them in a ring that moves with you. The trick was to smooth the velocity before using it, so the fog doesn't snap every time you change direction:
CENSORED
That small anticipation term pushes the emitter slightly ahead of you, so there is always fog waiting where you are about to step.

Two layers, two shaders
We split the fog into an upper layer and a ground layer, each with its own shader. The ground layer hugs the floor and reacts faster; the upper one drifts slowly and gives depth.
- Upper layer: large, soft particles that fade with distance.
- Ground layer: small, dense particles with short lifetimes.
- Density bursts when you stop moving, so standing still feels a little less safe.
Performance
All of this had to run on modest hardware. After tuning particle counts and lifetimes, the streets hold 90+ FPS on a GTX 1660, which was our target.
Next up: making sure the fog plays nicely with the lighting in the rest of the zones.