Builtin Library
README.md
shutterBlur
The render passes behind the MotionBlur component and the motionBlur module.
Three compute passes after lighting.
mb_tilemax reduces the velocity buffer to one dominant velocity per 32x32
tile, already scaled by the shutter fraction and clamped to the longest blur
allowed.
mb_neighbormax takes the dominant velocity across each tile's 3x3
neighbourhood. This is the pass that lets a moving object blur past its own
silhouette: a static background pixel beside something fast has no velocity of
its own, and a filter that reads only that pixel leaves it sharp, so the blur
stops dead at the object's edge. Reading the neighbourhood tells the pixel which
direction something nearby was travelling, so it can go looking for what swept
over it. A diagonal neighbour counts only when it is travelling toward the tile
— taking every diagonal is what puts a square halo around a fast object.
mb_gather reconstructs. Each tap carries a weight built from three
separable cases (McGuire et al., A Reconstruction Filter for Plausible Motion
Blur): the tap is in front of this pixel and its own blur is long enough to
reach here; the tap is behind and this pixel's blur moved off it, revealing it;
or both sit at a similar depth and are blurred together. Keeping the three apart
is what leaves a motion boundary readable — one blended weight averages a sharp
background into a moving foreground and the edge dissolves into mush.
Taps alternate between walking the neighbourhood's dominant velocity and this pixel's own. Both directions are needed: the first finds what swept over the pixel from elsewhere, the second finds the pixel's own smear. A pixel that moved less than a pixel has no direction of its own worth walking and uses the neighbourhood's for every tap. Tap positions are jittered per pixel by interleaved gradient noise so they land as noise rather than as banded copies of the moving object.
The centre pixel is weighted by how little it moved, so a nearly-static pixel keeps most of itself and a fast one is mostly made of its neighbours. A flat centre weight would outvote every tap and the blur would never appear.
The blur can never be longer than a tile: the neighbourhood search reaches one
tile in each direction, so a velocity longer than that would have contributors
the search never visits. That is the ceiling behind maxRadius.
Depth is linearised through the drawn camera's own inverse view-projection, so the passes are correct in offscreen captures and RTT cameras at their own resolutions. The tile targets are screen-sized at one texel per tile, so the grid follows a window resize instead of pinning to whatever the window was when the feature started.
Parameters arrive through the motion_blur_params buffer, packed by the
motionBlur module.