Fog
Light being absorbed and scattered by a medium between camera and surface. Gives depth cues, mood, hides draw distance, and makes light shafts ("god rays") visible.
Core formula for all of them: final = surface * transmittance + inscattered_light,
with transmittance = exp(-density * distance) (Beer–Lambert). The techniques differ in how
much of the medium and lighting they actually evaluate.

The baseline: height-fog medium with froxel volumetrics. Without fog: fog-none.
At a glance
| Technique | Looks like | Cost | Best for |
|---|---|---|---|
| Distance / height fog | Uniform haze, thicker in valleys | very low | Every game, as the base layer |
| Fog cards | Hand-placed wisps and layers | low–med (overdraw) | Art-directed local mist |
| Raymarched volumetric | Light shafts, local fog volumes | med–high | Few hero lights, one camera |
| Froxel volumetric | Shafts + haze from all lights, stable | med (fixed) | Many lights, multiple cameras |
Distance / height fog
- Looks like: Colour fades to fog colour with distance; exponential height variant pools in low areas.
- How:
- Per pixel (in the object shader or as a post pass using depth) compute view distance.
- Linear,
exporexp²falloff → fog factor; lerp to fog colour. - Height variant: analytic integral of density
d(h) = d0 * exp(-falloff * h)along the view ray.
- Strong: Nearly free, analytic, no artefacts, hides far clip / LOD pops.
- Weak: No lighting interaction (no shafts, no shadows in fog); flat look.
- Needs: Depth (post-process) or world position (in-shader).
- Cost: very low.
- Variants:
- Directional inscattering: tint fog towards sun colour when looking at the sun (cheap "sun glow").
- Sample a blurred sky cubemap for fog colour so it matches the sky.
- Seen in: Nearly everything; Unreal's Exponential Height Fog.
- Reference code: height-fog.js

Height fog (top) vs. linear distance fog (bottom), same view. Without fog: fog-outdoor-none.
Fog cards
- Looks like: Soft, drifting mist layers placed by artists.
- How: Camera-facing or world-aligned quads with a soft noise texture; soft-particle fade against depth; fade out near camera.
- Strong: Full art control, cheap to reason about, animates easily.
- Weak: Overdraw; intersections and camera-near popping; looks flat from some angles.
- Needs: Depth texture (soft particles), particle system or placed quads.
- Cost: low–med, dominated by overdraw.
- Seen in: Most games' ground mist, caves, swamps.
- Reference code: fog-cards.js

Raymarched volumetric (screen space)
- Looks like: Visible light shafts, shadows cast through fog, local fog volumes.
- How:
- Per pixel (often at ½ or ¼ res), march from camera to depth in N steps.
- At each step: density (constant/noise/volume shape) × light (sample shadow map) × phase function.
- Accumulate scattering + transmittance; jitter start per pixel + temporal/bilateral blur to hide banding; upsample.
- Strong: Exact per-pixel, easy to add noise and local volumes, simple to start.
- Weak: Cost scales with resolution × steps × lights; banding/noise without temporal filtering; every camera pays again.
- Needs: Depth, shadow maps, a phase function (Henyey–Greenstein), blue noise + temporal filter.
- Cost: med–high.
- Variants: Radial-blur "god rays" (screen-space fake from bright pixels, no march) as the cheapest fake.
- Seen in: Killzone Shadow Fall, many engine-store volumetric fog assets.
- Reference code: raymarch.js (half res, 48 steps, sun only; sun depth map in sun-shadow.js)

Froxel volumetric
- Looks like: Consistent haze and shafts from all lights, soft and stable.
- How:
- A 3D grid aligned to the camera frustum (froxels, e.g. 160×90×64, depth slices distributed logarithmically).
- Inject: compute pass evaluates density + every light (with shadows) at each froxel centre.
- Integrate: march the grid front to back → accumulated scattering + transmittance per slice.
- Composite: per pixel, look up the grid at the pixel's depth;
colour * T + S. - Temporal reprojection of the grid to hide low resolution.
- Strong: Cost set by grid, not screen resolution; all lights in one primitive; transparents can sample the grid too.
- Weak: Low-res → blurry shafts, light leaking through thin geometry; temporal ghosting; compute + 3D textures needed.
- Needs: Compute shaders, 3D textures, shadow maps, a light list, temporal history per camera.
- Cost: med, roughly fixed.
- Variants: Density from volume shapes / 3D noise; per-light volumetric intensity; art-direction via medium albedo and anisotropy ("haze machine" dial).
- Seen in: Frostbite, Unreal Volumetric Fog, Red Dead Redemption 2, Assassin's Creed IV.
- Reference code: froxel.js (240×135×96, sun + point lights; no temporal reprojection, so shafts are softer and slightly blocky near walls). Image at the top.
Baseline
- Always on: exponential height fog with sun-direction inscattering and sky-matched colour. Essentially free, and far better-looking than plain linear distance fog.
- When lights should read in the air: froxel volumetrics (+ temporal reprojection). The cost is fixed and stable, and all lights are handled in one system. Raymarching is quicker to prototype but gets expensive with every light and camera you add.
- Art direction on top: fog cards for local wisps, ground mist and caves.
- Cheaper fallbacks: screen-space radial "god rays" for a single sun on low-end targets.
Related
- Outlines
- Fog of war is a gameplay visibility technique, not atmosphere — belongs in a separate topic.
References
- Bart Wroński — Volumetric Fog: Unified Compute Shader-Based Solution to Atmospheric Scattering (SIGGRAPH 2014).
- Sébastien Hillaire — Physically Based and Unified Volumetric Rendering in Frostbite (SIGGRAPH 2015).
- Iñigo Quílez — Better Fog (analytic height fog).