shader-postprocessing

Installation
SKILL.md

Shader Postprocessing

When to use this

  • Your 3D scene needs a full-screen effect applied after the main render (bloom on bright/emissive materials, chromatic aberration for a lens-like feel, film grain for texture, vignette for framing, depth of field for focus falloff).
  • You need to chain multiple such effects together in a defined order, since each depends on reading the previous pass's output.
  • You want to write a custom full-screen effect (e.g. a stylized color grade, a glitch effect) that isn't covered by a stock pass.
  • Do NOT reach for postprocessing as a first step when a simpler in-shader or in-material solution exists: MeshStandardMaterial.emissive plus tone mapping alone often gets you most of the "glow" people reach for UnrealBloomPass for; a CSS box-shadow/filter on a DOM overlay is far cheaper than a full postprocessing pipeline for effects on 2D UI. Only add a compositor pass when the effect genuinely requires reading rendered pixel data (bloom needs to know what's bright, DOF needs a depth buffer, grain needs to composite over the final image).

Mental model

Every post-processing effect works the same fundamental way: instead of rendering directly to the visible canvas, you render the scene into an off-screen texture (a render target / framebuffer), then run one or more full-screen fragment shader passes that read that texture as input and write a transformed result to another render target, chaining passes until the final one writes to the actual screen. EffectComposer (from Three.js's examples, or the more actively maintained third-party postprocessing library) manages this chain of render targets and passes for you, handling the ping-pong buffer swapping between passes automatically.

The cost model to internalize: every pass is a full additional render of the entire screen's worth of pixels, at whatever resolution the composer is configured for. A scene with a 3-pass chain (render, bloom, vignette) is not "the same cost plus a little," it is 2-3x the fragment shader work of the base scene render, and some effects (bloom in particular) are internally multi-pass themselves (bright-pass extraction, then several blur passes at different mip levels, then a composite pass), meaning UnrealBloomPass alone can be 4-6 effective full-screen passes.

Bloom specifically works by extracting only the pixels above a brightness threshold into a separate buffer, blurring that buffer heavily (often at progressively downsampled resolutions for a cheap large-radius blur, this is why quality bloom implementations use mip-mapped blur chains), then additively compositing the blurred bright regions back over the original image. This is why bloom on a scene with no genuinely bright/emissive materials does nothing visible: there's nothing above the threshold to extract.

Depth of field needs a linear depth buffer as an additional input beyond color, meaning the base scene render must write depth (usually automatic via WebGLRenderTarget's depth texture attachment), and the DOF pass uses that depth to compute a per-pixel blur radius based on distance from a focal plane, making it one of the more expensive stock effects since the blur itself is often a large-kernel separable blur applied per depth region.

The practical discipline this skill is about: know the per-effect cost, know what visual problem each one actually solves, and be willing to say a requested effect isn't worth its frame budget for a given target device tier, rather than chaining five passes because they're each individually easy to add.

Installs
2
First Seen
Aug 30, 2026
shader-postprocessing — avnehsbhatia/ultraui