Skip to content

About

A Gaussian blur for URP RenderGraph that stays fast, accurate and stable at every strength.

Topics

Resources

Stars

2 stars

Watchers

0 watching

Forks

Repository files navigation

Stepless Gaussian Blur

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.

Goals

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.

Benchmarks

M4 GPU time and Gaussian error across blur strengths

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.

Installation

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.

Usage

Blur the camera

  1. Add the Stepless Gaussian Blur renderer feature to your Universal Renderer asset.
  2. Add Post-processing > Stepless Gaussian Blur to a Volume Profile.
  3. Set Blur Strength.
  4. 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.

Blur a texture

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.

Blur in your own RenderGraph pass

TextureHandle blurred = blur.Record(graph, source, sigma);
  • source is a texture in the graph, such as the camera color or an imported color-only RenderTexture. Blur other textures with Blur.
  • RecordReduced returns a smaller texture that bilinear sampling finishes.
  • RecordTo writes into a BlurTarget (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).

Tips

  • Call blur.WarmUpShaders(sourceFormat) during loading to avoid a hitch on the first blur; for the camera effect, call the feature's WarmUpShaders(cameraColorFormat). With a destination or target format you chose, use WarmUpShaders(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.

How it works

Below sigma 1.62

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.

One 2D Gaussian pass from source to result Gaussian X to a full-resolution intermediate, then Gaussian Y

Sigma 1.62–2.40

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.

Fitted down to half resolution, then fitted up Fitted down to half resolution, a Gaussian there, then fitted up

Sigma 2.40–16.1

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.

One level: binomial down to half resolution, a Gaussian, cubic up Two levels: binomial down to quarter resolution, a Gaussian, cubic up, then cubic turning bilinear up Three levels: binomial down to eighth resolution, a Gaussian, cubic up twice, then bilinear up

Sigma 16.1–24

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×.

4× down to quarter, binomial down to eighth resolution, Gaussian X and Y, cubic up to quarter, bilinear up to full

Sigma 24 and above

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.

4× down to quarter, binomial down to eighth and sixteenth resolution, Gaussian X and Y, cubic up to quarter, bilinear up to full

All layouts

All weights are nonnegative, so there are no dark rings.

Further reading

  • 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.

License

Released under the MIT License.

References

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.

About

A Gaussian blur for URP RenderGraph that stays fast, accurate and stable at every strength.

Topics

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages