Distance & height fog
Fade what is far away, or low down, toward a fog color.
Cards
One live example to tweak, learn and copy.
How it works
Air is never perfectly clear. Over a long enough stretch, dust and water droplets absorb and scatter part of the light coming from a distant object, and scatter some of the sky’s light toward you instead. Far mountains look paler and bluer than near ones for that reason, and painters call it aerial perspective. Fog in a shader fakes it with two numbers per pixel: how far the surface is, and how much of its light survives that distance.
The survival part follows the Beer–Lambert law: every unit of fog lets the same fraction of the light through, so the
transmittanceThe fraction of light that gets through a medium such as fog, from 1 (all of it) to 0 (none). In an even medium it falls exponentially with the distance crossed. Transmittance The fraction of light that gets through a medium such as fog, from 1 (all of it) to 0 (none). In an even medium it falls exponentially with the distance crossed. Linear interpolation (mix) Blends from a to b by a factor t, along a straight line. GLSL calls it mix; HLSL and most node editors call it lerp.T = exp(−density · distance)exp(-density * distance). The
pixel then mixesBlends from a to b by a factor t, along a straight line. GLSL calls it mix; HLSL and most node editors call it lerp.mix(a, b, t) = a (1 − t) + b t
Where the distance comes from decides the stage:
- Screen-post. A full-screen pass reads the finished frame and the depth bufferAn image the size of the framebuffer that holds, for each pixel, how far away the nearest surface drawn so far is. The depth test uses it to hide what is behind.Rendering
Depth buffer
An image the size of the framebuffer that holds, for each pixel, how far away the nearest surface drawn so far is. The depth test uses it to hide what is behind.
Sources, turns each pixel’s depth into a distance and fogs the whole image at once. One pass covers every material in the scene, which is why engines do it this way. Exponential fog is this version. - Surface. Each material computes its own fog from the world position of the fragment and the camera position, at the end of its fragment shader. It needs no depth texture and works on transparent surfaces, which a depth pass misses, but every shader in the scene has to include it.
Height fog swaps the even medium for one that is thicker near the ground: the density falls exponentially with altitude, and the fog along a ray still has a closed form. It needs the world position of each pixel, so in a post pass it also needs the camera’s matrices to rebuild that position from depth.
Most engines ship distance fog, and many height fog too, in their environment settings, and they are usually the right choice. Write your own when the look has to differ: a fog color that changes with the view direction, fog that only fills a valley, or fog in a stylized pipeline where the built-in one doesn’t run.
Further reading
- mix (opens in a new tab)Reference
- clamp (opens in a new tab)Reference
- Linear interpolation (opens in a new tab)Reference
- Shaping functions (opens in a new tab)Book
- Useful little functions (opens in a new tab)Article
- pow (opens in a new tab)Reference
- Depth testing (opens in a new tab)Article
- glDepthFunc (opens in a new tab)Reference
- Spatial shaders (opens in a new tab)Docs
- Better fog (opens in a new tab)Article
- Beer–Lambert law (opens in a new tab)Reference
- glFog (opens in a new tab)Reference
- exp (opens in a new tab)Reference