A Gaussian blur for URP RenderGraph that stays fast, accurate and stable at every strength.
stepless-demo.mp4
Use it as a camera effect, inside your own RenderGraph passes, or to blur a texture once and reuse it.
Most blurs are fast or accurate in one range of strengths and compromise in the rest. Stepless aims to hold up across the whole range:
- Fast at small strengths, where a pyramid can't reduce resolution yet.
- Even cost across strengths.
- Any sigma, so strength can be animated smoothly instead of stepping between pyramid levels.
- Stable in motion: a moving highlight follows its true position instead of jumping with a coarse grid.
- Close to a true Gaussian.
- Fragment shaders only. Compute relies on shared memory, which some mobile GPUs don't keep on chip.
- One implementation for every GPU, with no per-vendor paths.
On M4 at 4K, blurring a photographed scene:
- Stepless's cost stays in a narrow band, highest at small strengths and falling as the blur widens. Skia and MPS jump in cost where they switch internal levels.
- At wide strengths, Stepless takes about half the time of Original Dual Blur, and it also reaches the strengths between Dual Blur's levels.
- Stepless stays close to a Gaussian except at sigma 1.4–2.3, where its error peaks at 6% near 1.6. Skia's error grows large at wide strengths and depends on where a highlight falls on its coarse grid.
Benchmark details cover the iPhone, Adreno, Xclipse, Immortalis and Mali GPUs, texture writes and methods.
Requires Unity 6000.3 or later and URP 17.3 with RenderGraph (Compatibility Mode off).
In the Package Manager, choose Install package from git URL:
https://github.com/aki-null/SteplessGaussianBlur.git
Or add it to Packages/manifest.json:
"net.aki-null.stepless-gaussian-blur": "https://github.com/aki-null/SteplessGaussianBlur.git"Append #<tag> to pin a release.
- Add the Stepless Gaussian Blur renderer feature to your Universal Renderer asset.
- Add Post-processing > Stepless Gaussian Blur to a Volume Profile.
- Set Blur Strength.
- Enable Post Processing on the camera.
Strength is sigma in pixels on an image whose shorter side is the feature's Reference Resolution (default 1024). Screen Space - Camera canvases are blurred with the scene; use Screen Space - Overlay for UI that stays sharp.
var blur = new GaussianBlur();
var target = new BlurTarget();
rawImage.texture = target.Texture; // stays valid; the target resizes in place
blur.Blur(source, target, sigma); // runs before the next camera draws; call again on changes
// When done:
blur.Dispose();
target.Dispose();Sigma is in source pixels. Use new BlurTarget(fullSize: true) if you
read the pixels or sample without filtering.
TextureHandle blurred = blur.Record(graph, source, sigma);sourceis a texture in the graph, such as the camera color or an imported color-only RenderTexture. Blur other textures withBlur.RecordReducedreturns a smaller texture that bilinear sampling finishes.RecordTowrites into aBlurTarget(one per camera) or a texture the size of the source's active image.- When the image fills only part of its texture, pass
((BlurSource)source).WithViewport(size).
- Call
blur.WarmUpShaders(sourceFormat)during loading to avoid a hitch on the first blur; for the camera effect, call the feature'sWarmUpShaders(cameraColorFormat). With a destination or target format you chose, useWarmUpShaders(sourceFormat, destinationFormat). - Alpha is blurred like color. Use premultiplied color for compositing.
- FP16 blurs lose a little brightness on some GPUs. For a blur that feeds back into itself every frame, use a float32 target.
Usage details covers viewports, borders, reduced output, formats, XR and warmup.
One full-resolution 2D Gaussian pass up to sigma 0.75, then Gaussian X and Y passes. From 1.46 the kernel morphs into the next layout's down/up filter.
A fitted downsample to half resolution and a fitted upsample back. Their weights and positions move with sigma; from about 1.72 a half-resolution Gaussian adds the rest.
A pyramid: binomial downsamples until the remaining blur fits a small Gaussian, then cubic upsamples. Levels crossfade in at sigma 4.22 and 8.55. From 4.25 to 4.5 the final upsample turns bilinear, one fetch instead of four.
A fixed six-pass bridge: quarter, then eighth resolution, a Gaussian, then cubic upsampling to 1/4 and bilinear to full. It is the wide layout one level finer: its halve starts as the pyramid's, and its cubic upsample is 2× instead of 4×.
Reduce to 1/16 resolution, blur, then upsample in two steps: cubic to 1/4, bilinear to full. At sigma 70, 140, … the Gaussian blends into the next coarser level.
All weights are nonnegative, so there are no dark rings.
- Usage details: source sizing, borders, reduced output, formats, XR, warmup.
- Design: how the filters fit together, conventions and vocabulary, for changing the code.
- Running tests.
Released under the MIT License.
Marius Bjørge introduced the Dual filter (here "Dual Blur", also called Dual Kawase), building on Masaki Kawase's blur ("Frame Buffer Postprocessing Effects in DOUBLE-S.T.E.A.L (Wreckless)", GDC 2003). The benchmarks compare against Skia's GPU blur and Apple's MPSImageGaussianBlur. For interactive comparisons of blur techniques, see Wladislav Artsimovich's Video Game Blurs.