Blur
Replace each pixel with a weighted average of its neighbors, the building block of bloom, depth of field and frosted glass.
Cards
One live example to tweak, learn and copy.
How it works
A blur gives every pixel a little of its neighbors’ color. In shader terms it is a kernelA small grid of weights. Each output pixel is the weighted sum of its neighbors, which blurs, sharpens or finds edges depending on the weights. Kernel A small grid of weights. Each output pixel is the weighted sum of its neighbors, which blurs, sharpens or finds edges depending on the weights. Texel One element of a texture, the texture's version of a pixel. A sample blends nearby texels or returns the nearest one.out = Σ weight(i, j) · texel(x + i, y + j)texel size = 1 / textureSize(sampler, 0)
The hard part is cost, not the math. Every tap is a texture read, and a wide blur needs many. Three tricks keep it affordable, and most real blurs use all of them:
- Separable passes. A Gaussian over a square equals a blur along the rows followed by a blur along the columns.
That turns
n × nreads inton + n, at the price of a second pass and a render targetA texture a draw writes into instead of the screen, so a later pass can read the result. Blur, bloom and every multi-pass effect use them.RenderingRender target
A texture a draw writes into instead of the screen, so a later pass can read the result. Blur, bloom and every multi-pass effect use them.
Sourcesbetween them. - Smaller images. A blur on a half-size copy covers twice the radius in pixels of the original, over a quarter of the pixels. Bloom and depth of field blur at half size or smaller.
- Bilinear taps. One read placed between two texels returns both, blended by the hardware, so a 9-tap kernel needs 5 reads.
Blur runs in two places. Over the finished frame it is a post-processFull-screen passes run in order over the finished frame, each reading the image the previous one wrote. Bloom, tone mapping and color grading live here. Post-process chain Full-screen passes run in order over the finished frame, each reading the image the previous one wrote. Bloom, tone mapping and color grading live here.
Further reading
- Mipmap (opens in a new tab)Reference
- Anisotropic filtering (opens in a new tab)Reference
- textureLod (opens in a new tab)Reference
- Kernel (image processing) (opens in a new tab)Reference
- Gaussian blur (opens in a new tab)Reference
- Sobel operator (opens in a new tab)Reference
- texture (opens in a new tab)Reference
- texelFetch (opens in a new tab)Reference
- Using textures in WebGL (opens in a new tab)Docs
- Framebuffers (opens in a new tab)Article
- Bloom (opens in a new tab)Article
- Custom post-processing (opens in a new tab)Docs
- glFramebufferTexture2D (opens in a new tab)Reference
- EXT_color_buffer_float extension (opens in a new tab)Docs
- Efficient Gaussian blur with linear sampling (opens in a new tab)Article
- exp (opens in a new tab)Reference