Documentation
// URP UI Blur Pro · v1.0.0 · Unity 6 / URP 17+
How It Works
URP UI Blur Pro integrates with URP's rendering pipeline, captures the scene at a configurable point, runs a blur algorithm, and makes the result available for UI panels and 3D world objects to sample. The blur computes once per profile per frame — panels themselves do no blur work at draw time.
The blur only runs when at least one active BlurImage or BlurObject references that profile. Profiles with no active consumers cost nothing.
Geometry, skybox, and transparents render through URP as usual.
The Blur Renderer Feature captures the rendered scene at the point controlled by each profile's CaptureMode.
The captured image is processed by the selected algorithm. Up to 16 profiles can run simultaneously, each fully independent.
BlurImage (UI panels) and BlurObject (3D meshes) sample the blurred output and apply frost tint, brightness, desaturation, and optional normal-map distortion.
Requirements
| Requirement | Version |
|---|---|
| Unity | 6000.0 or later |
| Universal Render Pipeline | 17.0.0 or later |
| RenderGraph | Enabled in Project Settings → Graphics → URP Global Settings |
| Platforms | PC, Mobile — VR/XR not tested, not officially supported |
Quick Start
Tools → URPUIBlur → Setup Wizard. It also opens automatically on first import.BlurProfile asset to the Profile field, or click New to create one inline.Without Post Processing works with all Canvas modes and is the recommended default.Capture Mode
CaptureMode controls which point in URP's frame the background is captured at.
| CaptureMode | Render Event | Notes |
|---|---|---|
WithoutPostProcessing |
BeforeRenderingTransparents | Compatible with all Canvas modes and BlurObject. Use in most cases. |
WithPostProcessing |
AfterRenderingPostProcessing | Includes bloom, tonemapping, color grading. Screen Space Overlay only — using this with Screen Space Camera or World Space produces a black panel. |
WithoutPostProcessing and WithPostProcessing at the same time for different BlurImage components with no conflict — each branch is an independent pass-chain with its own RTHandles.Blur Methods
| Method | Cost | Best For |
|---|---|---|
| Dual Kawase | ●●○○○ | Frosted-glass UI — widest radius at lowest cost |
| Custom Kawase | ●●○○○ | Fast circular blur with tunable tap count |
| Poisson Disk | ●●●○○ | Cinematic bokeh — eliminates grid sampling artifacts |
| Gaussian Separable | ●●●○○ | Mathematically exact Gaussian circle |
| Tent / Hex | ●●●●● | Hexagonal lens bokeh — cinematic moments |
| Radial | ●●○○○ | Zoom blur, impact effects, cutscenes |
BlurProfile
A BlurProfile is a serializable asset describing how to blur — algorithm, pass count, and per-algorithm parameters. It contains no appearance settings; those live on the BlurImage or BlurObject that references it. Up to 16 profiles can be active simultaneously.
Create a profile: Assets → Create → URPUIBlurPro → Blur Profile, or click New next to the Profile field on any BlurImage component.
Common parameters
| Property | Type | Range | Description |
|---|---|---|---|
blurMethod | BlurMethod | — | Algorithm used for this profile |
intensity | float | 0–1 | Blur strength multiplier. 0 = no blur, 1 = full authored strength. Scales parameters each frame without modifying serialized values. Shared across all BlurImages referencing this profile. |
iterations | int | 1–8 | Pass count / mip pyramid depth — more = wider blur |
blurOffset | float | 0–5 | Tap spread multiplier (scales with screen resolution) |
downsample | int | 1–8 | RT resolution divisor — auto-adjusted for screens wider than 1920 px |
Dual Kawase
Uses iterations and blurOffset. The mip pyramid is built automatically; no additional parameters needed.
Custom Kawase / Poisson Disk
| Property | Range | Description |
|---|---|---|
taps | 3–12 | Number of samples per pass |
useIGNRotation | bool | (Poisson only) Per-pixel sincos rotation — higher quality, minimal cost |
Gaussian Separable / Tent Hex
| Property | Range | Description |
|---|---|---|
gaussianRadius | 1–16 | Sample radius in pixels (scales with screen resolution, capped at 64) |
Radial
| Property | Range | Description |
|---|---|---|
radialTaps | 4–32 | Number of samples arranged around the ring |
radialRadius | 0.5–20 | Ring radius in texels (scales with screen resolution) |
Update Mode
Controls how often the blur pass chain re-runs. Skipped frames reuse the last blurred result at zero added cost.
| Property | Type | Description |
|---|---|---|
updateMode | BlurUpdateMode | EveryFrame (default) or Throttled |
throttleFps | int 0–120 | Max blur updates per second in Throttled mode. 0 = freeze: blurs once on activation, holds that frame, re-captures each time the panel reopens. |
| Mode | Use Case |
|---|---|
| EveryFrame | Live-moving content — game world, animated backgrounds |
| Throttled (1–120) | Semi-static UI over a mostly-still background. May ghost on fast motion. |
| Throttled (0, freeze) | Fully static backgrounds — pause menus, modals. Near-zero cost after first frame. |
BlurImage Component
BlurImage attaches a profile's blur output to a UI Graphic (Image, RawImage, etc.). It creates and manages a private Material instance using UI_Blur_Universal, enabling the correct slot keyword and syncing appearance lazily via a dirty flag.
Settings
| Field | Type | Description |
|---|---|---|
profile | BlurProfile | The blur recipe to sample. Supports up to 16 profiles simultaneously. |
captureMode | CaptureMode | Whether to include post-processing in the blur (see Capture Mode). |
frostTint | Color | Color tinted over the blurred background. |
frostStrength | float 0–1 | Tint blend weight (0 = no tint, 1 = full tint). |
brightness | float 0.8–1.5 | Brightness multiplier applied after tint. |
desaturate | float 0–1 | Desaturation amount (0 = full color, 1 = grayscale). |
edgeStrength | float 0.5–1 | Vignette darkening toward panel edges (1 = no vignette). |
tintColor | Color | Overall tint multiplied with vertex color. |
normalMap | Texture | Normal map driving glass surface distortion. |
bumpStrength | float 0–1 | Distortion intensity. |
normalMapTiling | Vector2 | Tiling of the normal map texture. |
normalMapOffset | Vector2 | Offset of the normal map texture. |
normalMode | NormalMode | UV calculation mode for the normal map (see below). |
Normal Mode
| Value | Description |
|---|---|
| StandardTile | Uses the Image's own UV directly. |
| LocalFixedScale | Scales with screen resolution — resolution-independent (default). |
| ScreenSpaceFix | Projected in screen space, aspect-corrected. |
Public API
// Force appearance fields to push into the material on the next Canvas repaint.
// Call this after changing appearance fields from script.
blurImage.MarkDirty();
// Animate profile.intensity from its current value to 1 over `duration` seconds.
// Reverses smoothly if called while a FadeOut is in progress.
// Uses unscaled time -- works during Time.timeScale = 0.
blurImage.FadeIn(0.4f);
// Animate profile.intensity from its current value to 0, then stop.
blurImage.FadeOut(0.4f);
FadeIn and FadeOut animate profile.intensity, which is per-profile. If multiple BlurImage components share the same profile, all of them fade together. Use a dedicated profile per panel to fade them independently.BlurObject Component
BlurObject attaches a profile's blur output to a MeshRenderer (windows, floors, glass panels in 3D space). It always uses WithoutPostProcessing capture — 3D transparent objects render during the transparent pass, so capturing any later would include the object's own draw call and produce a black surface.
Settings
| Field | Type | Description |
|---|---|---|
profile | BlurProfile | The blur profile to sample. |
frostTint | Color | Color tinted over the blurred background. |
frostStrength | float 0–1 | Tint blend weight. |
brightness | float 0.8–1.5 | Brightness multiplier applied after tint. |
desaturate | float 0–1 | Desaturation amount. |
refractionIndex | float 0–0.5 | IOR offset — how strongly the surface bends the background sample. |
normalMap | Texture | Surface detail driving refraction direction. |
bumpStrength | float 0–1 | Refraction bend intensity. |
tintColor | Color | Overall tint multiplied with vertex color. |
mainTex | Texture | Albedo texture blended over the blur result. Leave empty for plain frosted glass. |
Modifying Parameters at Runtime
All fields on a BlurProfile can be written at runtime. Changes take effect on the next rendered frame.
BlurProfile p = myBlurProfile;
// Continuous -- takes effect next frame, zero overhead
p.blurOffset = 2f; // Dual Kawase / Kawase / Poisson tap spread
p.gaussianRadius = 8; // Gaussian / Tent-Hex pixel radius
p.radialRadius = 6f; // Radial ring radius in texels
// Discrete -- takes effect next frame, no pass rebuild
p.iterations = 4;
p.taps = 8;
p.downsample = 2;
p.radialTaps = 16;
// Method -- triggers pass rebuild on next frame
// May produce a one-frame hitch on mobile. Change during loading screens.
p.blurMethod = BlurMethod.GaussianSeparable;
After changing appearance fields on BlurImage or BlurObject from script, call MarkDirty() to push the changes to the material:
blurImage.frostTint = new Color(0.8f, 0.9f, 1f, 1f);
blurImage.frostStrength = 0.5f;
blurImage.brightness = 1.1f;
blurImage.MarkDirty();
Intensity & Fade
profile.intensity scales all blur-strength parameters each frame without modifying the serialized values. Unlike toggling isActive, intensity 0 keeps the pass alive so there is no one-frame black flash when the blur returns.
| Scaled | Not Scaled |
|---|---|
blurOffset, gaussianRadius, radialRadius, iterations, downsample |
blurMethod, taps, radialTaps, useIGNRotation |
// Set directly
myProfile.intensity = 0.5f;
// Or via the typed API
myProfile.SetIntensity(0.5f);
float current = myProfile.GetIntensity();
// Built-in coroutine fade on BlurImage
blurImage.FadeIn(0.4f); // current -> 1 over 0.4s, unscaled time
blurImage.FadeOut(0.3f); // current -> 0 over 0.3s, unscaled time
intensity is per-profile, not per-BlurImage. Multiple BlurImage components sharing the same profile are all affected simultaneously. Use a dedicated profile per panel to control them independently.Update Mode
Use updateMode and throttleFps to reduce how often the blur pass re-runs. Skipped frames reuse the persistent RTHandle untouched — zero added cost beyond the BlurImage's own sample.
// Throttle to 30 blur updates/sec
myProfile.updateMode = BlurUpdateMode.Throttled;
myProfile.throttleFps = 30;
// Freeze: blur once on activation, hold until panel closes and reopens
myProfile.updateMode = BlurUpdateMode.Throttled;
myProfile.throttleFps = 0;
// Return to every-frame blur
myProfile.updateMode = BlurUpdateMode.EveryFrame;
Common Patterns
Pause menu with blur fade
[SerializeField] BlurImage _blurImage;
void OpenMenu()
{
gameObject.SetActive(true);
_blurImage.FadeIn(0.4f);
Time.timeScale = 0f;
}
void CloseMenu()
{
_blurImage.FadeOut(0.3f);
Time.timeScale = 1f;
}
Manual intensity animation
[SerializeField] BlurProfile _profile;
float _target;
void Update()
{
float current = _profile.GetIntensity();
_profile.SetIntensity(Mathf.MoveTowards(current, _target, Time.deltaTime * 4f));
}
void OnMenuOpen() => _target = 1f;
void OnMenuClose() => _target = 0f;
Quality preset switching
Switch algorithm and parameters during scene load to match player graphics settings.
[SerializeField] BlurProfile _profile;
void ApplyQuality(int level)
{
switch (level)
{
case 0: // Low
_profile.blurMethod = BlurMethod.DualKawase;
_profile.iterations = 2;
_profile.downsample = 4;
break;
case 1: // Medium
_profile.blurMethod = BlurMethod.DualKawase;
_profile.iterations = 3;
_profile.downsample = 2;
break;
case 2: // High
_profile.blurMethod = BlurMethod.PoissonDisk;
_profile.iterations = 4;
_profile.downsample = 2;
break;
}
}
Battery saver mode
Disable all BlurImage components to stop the blur from running entirely.
BlurImage[] _allImages;
void Awake()
{
_allImages = FindObjectsByType<BlurImage>(
FindObjectsInactive.Include, FindObjectsSortMode.None);
}
void SetBatterySaver(bool enabled)
{
foreach (var img in _allImages)
img.enabled = !enabled;
}
Known Limitations
These are by-design constraints from how URP's RenderGraph schedules render passes. They are not bugs.
WithoutPostProcessing captures at BeforeRenderingTransparents. Any 3D object that hasn't rendered yet at that point is entirely absent from the captured texture.
Workaround: use WithPostProcessing on a Screen Space Overlay canvas — it captures after all geometry has finished rendering.
Unity draws Screen Space Camera canvases on top of World Space canvases regardless of scene hierarchy. Additionally, the two canvas modes capture at different stages, so neither blur can see the other's panel content.
There is no workaround for this combination. Change the Screen Space Camera canvas to Screen Space Overlay instead — Overlay always renders after World Space geometry, so layering and blur capture both work correctly.
When two BlurImage panels are rendered by the same camera, both
capture at BeforeRenderingTransparents — the same moment in
the same frame. The front panel's blur runs before the back panel has been
composited into the color buffer, so it samples the raw scene rather than the
already-blurred backdrop.
Screen Space Overlay: no workaround. All Overlay canvases render through the same implicit camera and share the same capture point.
Workaround — separate canvases on separate cameras via Camera Stack:
place each panel on its own Screen Space Camera canvas, each
assigned to a different camera. Stack the cameras so the popup camera renders
after the backdrop camera. The popup's BlurImage then captures
a color buffer that already contains the backdrop blur.
- Keep your Main Camera with Culling Mask set to Everything.
- Add a second camera. Set Render Type to Overlay and Culling Mask to UI only. Match projection, FOV, and clipping planes to the Main Camera.
- Make the new camera a child of the Main Camera and zero its local position and rotation.
- In the Main Camera's Camera Stack, add the new camera as the last entry.
- Set the backdrop canvas to Screen Space Camera, Render Camera = Main Camera.
- Set the popup canvas to Screen Space Camera, Render Camera = Stack Camera.
Both canvases must use Screen Space Camera mode — placing
both panels on the same canvas or the same camera will not work even with a
Camera Stack. Use a separate BlurProfile per panel to control
each independently.
Overlay + WithPostProcessing captures after all geometry — opaque, transparent, world-space, and 3D objects — and includes post-processing effects. It is the most reliable option when you need the blur to include all scene content.
Troubleshooting
• Confirm the Blur Renderer Feature is added and enabled on the active URP Renderer asset.
• Ensure a BlurProfile is assigned to the BlurImage component.
• If using WithPostProcessing, confirm the Canvas is set to Screen Space Overlay.
• Reduce downsample — high values produce a low-resolution RT that becomes visible at low blur strength or when intensity is below 1.
• Increase iterations or blurOffset to widen the blur and soften the pixelation.
• Check updateMode on the profile — Throttled with throttleFps = 0 intentionally freezes the blur after the first frame.
• Confirm at least one BlurImage referencing the profile is enabled and visible — profiles with no active consumers stop running automatically.
• The distortion UV is sampling outside the blur RT's scissor region. The scissor automatically expands by bumpStrength to cover the distortion range — confirm bumpStrength is not larger than the panel's screen-space margin allows.
• Move the panel away from screen edges, or reduce bumpStrength.
• Switch to Dual Kawase (lowest cost per quality).
• Increase downsample to 4 and reduce iterations to 2.
• See Performance Tips below for profiling-level optimisations.
• Open it manually via Tools → URPUIBlur → Setup Wizard.
• If no renderers appear, create a URP Universal Renderer asset first: Assets → Create → Rendering → URP Universal Renderer.
• The Scene view camera is separate from the Game camera and may capture at a different resolution or with different post-processing. Always treat Game view as the reference.
• If the Scene view blur looks distorted, this is expected — the scissor rect is computed from the Game camera's viewport, not the Scene camera's.
Performance Tips
These optimisations apply to any platform, not just mobile. Each active profile runs its full pass chain every frame it is needed — reducing what runs and how often has the most impact.
If multiple panels need identical blur parameters, assign them the same BlurProfile. The pass chain runs once and all panels sample the same result — zero additional GPU cost per extra panel.
Set updateMode to Throttled for panels over a mostly-still background. Skipped frames reuse the last blurred result at zero added cost.
Set throttleFps to 0 to freeze the blur after its first frame — ideal for pause menus, modals, and any background that does not move while the panel is open.
Each distinct CaptureMode branch (WithoutPostProcessing / WithPostProcessing) is a separate pass chain with its own RTHandles. Mixing capture modes on the same profile doubles the GPU and memory cost for that profile.
Each active profile is an independent pass chain. Consolidate panels that share parameters into fewer profiles rather than giving every panel its own profile.
A profile's pass chain stops running automatically when all its BlurImage consumers are disabled or invisible. Disable the component (not just the GameObject's alpha) when a panel is fully hidden to eliminate its cost entirely.
Third-Party Credits
Dual Kawase Blur — algorithm by Marius Bjørge, presented at GDC 2015 (ARM).