Documentation
// UI Blur Pro for URP · v1.0.0 · Unity 6 / URP 17+
How It Works
UI Blur Pro for URP 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 → UIBlurPro → 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.You can also create profiles directly from the Blur Renderer Feature inspector by clicking Create New Profile next to any slot in the profiles list.
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 render textures.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-burst blur, impact effects, cutscenes — not a soft bokeh |
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.
Ways to create a profile: Assets → Create → UIBlurPro → Blur Profile, click New next to the Profile field on any BlurImage component, or click Create New Profile next to any slot in the Blur Renderer Feature's profiles list.
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
Zoom-burst blur: streaks radiate from a focal point, strongest at the edges and vanishing toward the point itself. radialInnerRadius / radialOuterRadius control exactly where that sharp-to-blurred transition happens. This pass draws with a single material, so it has one focal point per frame — by default the screen center; enabling radialFocusOnPanelCenter moves that point to the first panel's rect center instead. Best suited to a full-screen effect or a preset used by a single panel. If you need several panels each with their own independent zoom-burst center, give each one its own BlurProfile.
The scissor padding needed to cover the outward streak (driven by radialZoomStrength) scales with each panel's own on-screen size rather than the screen size — a small panel keeps a small, cheap scissor rect even at high radialZoomStrength instead of padding out toward full-screen.
| Property | Range | Description |
|---|---|---|
radialTaps | 4–32 | Number of samples taken along each streak |
radialZoomStrength | 0–0.5 | Fraction of distance-to-focal-point pulled per pixel (resolution-independent) |
radialInnerRadius | 0–1.2 | Normalized distance from the focal point below which the image stays sharp |
radialOuterRadius | 0–1.2 | Normalized distance from the focal point beyond which blur is at full strength |
radialFocusOnPanelCenter | bool | Off = focal point at screen center; on = focal point at the first panel's rect center (still one shared point) |
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. |
| Panel type | Suggested Update Mode |
|---|---|
| Damage/speed feedback, fast-moving content | EveryFrame |
| HUD (health bar, minimap) | Throttled, 30–60 fps |
| Inventory / crafting menu | Throttled, ~10 fps |
| Pause menu / modal | Throttled, 0 fps (freeze) |
Re-blur frequency can never exceed your game's actual frame rate, so setting throttleFps higher than needed is harmless. When sharing one profile across panels with different needs, use the highest rate any of them actually requires rather than creating a separate profile just for a different throttleFps.
BlurObject is only "active" while it's actually visible to the camera, which can flicker as the camera or object moves. If such a BlurObject shares a profile with a Throttled/frozen BlurImage panel, the object's visibility flicker keeps resetting the shared clock — the panel keeps re-blurring (correctly, with no visible glitch) far more often than its throttleFps suggests, quietly giving up the performance savings freezing was meant to provide. Give the panel its own dedicated profile if you need it to actually stay at its throttled/frozen cost while sharing a scene with such an object.Throttled/frozen for panels that stay in one place while visible (pause menus, modals) — not ones with slide-in/slide-out or drag animations.BlurImage Component
BlurImage attaches a profile's blur output to a UI Graphic (Image, RawImage, etc.). It creates and manages a private Material instance, 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). |
vignetteColor | Color | Color blended in toward the panel edges. Alpha is ignored — use vignetteStrength for blend amount. |
vignetteStrength | float 0–1 | How strongly vignetteColor blends in toward the edges (0 = no vignette). |
normalMap | Texture | Normal map driving glass surface distortion. |
bumpStrength | float -1–1 | Distortion intensity. Positive = convex bulge, negative = concave dish. |
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). |
edgeDistancePixels | float 0–50 | Width of the procedural edge bevel, in on-screen pixels — stays a consistent visual width regardless of panel size or resolution. |
heightStrength | float 0–1 | Strength of the procedural edge bevel distortion. |
contentNormalStrength | float -1–1 | Treats the image's own content as a pseudo normal map for a subtle ripple/refraction look. 0 (default) is off — most noticeable on full-bleed photos/illustrations. |
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.radialZoomStrength = 0.15f; // Radial: zoom-burst strength p.radialInnerRadius = 0f; // Radial: sharp region radius (0-1.2) p.radialOuterRadius = 0.7f; // Radial: full-blur region start (0-1.2) // Discrete -- takes effect next frame, no pass rebuild p.iterations = 4; p.taps = 8; p.downsample = 2; p.radialTaps = 16; p.radialFocusOnPanelCenter = false; // false = screen center, true = first panel's center // 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, radialZoomStrength, 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 render texture 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.
Screen-space blur has no concept of depth — each pass blurs neighboring pixels in the captured texture regardless of what the camera considers in front of or behind the panel. An opaque object standing between the camera and a world-space panel (a BlurObject, or a BlurImage on a World Space / Screen Space Camera canvas) can bleed its color into the blur near the panel's edges, even at pixels the object doesn't actually occlude — most noticeable with bright or highly saturated objects. This is inherent to screen-space blur convolution in general, not specific to any one algorithm here.
Workaround: keep strongly emissive or brightly lit objects a little further from panel edges, or lower the blur radius/iterations on that profile if the bleed is noticeable.
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 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 → UIBlurPro → Setup Wizard.
• If no renderers appear, create a URP Universal Renderer asset first: Assets → Create → Rendering → URP Universal Renderer.
• This package's Materials are created purely at runtime, so no .mat asset in your project directly references these shaders — Unity's build pipeline strips any shader nothing references, which would otherwise remove them from the build even though everything works fine in the Editor.
• The bundled Runtime/Resources/ folder contains one dummy Material per shader specifically to prevent this — anything inside a Resources folder is always included in a build. Don't delete or move these Materials out of Resources/.
• If you still see this after confirming the Resources/ folder is intact, do a full clean Player build (delete Library/ShaderCache or the whole Library folder) so shaders recompile from scratch.
• 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 render textures. 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).