Skip to content

Instantly share code, notes, and snippets.

@JohannesMP
Last active October 26, 2024 07:16
Show Gist options
  • Save JohannesMP/7d62f282705169a2855a0aac315ff381 to your computer and use it in GitHub Desktop.
Save JohannesMP/7d62f282705169a2855a0aac315ff381 to your computer and use it in GitHub Desktop.
UI.Image Blur Shader with layering and masking support
Shader "Custom/UIBlur"
{
Properties
{
[Toggle(IS_BLUR_ALPHA_MASKED)] _IsAlphaMasked("Image Alpha Masks Blur", Float) = 1
[Toggle(IS_SPRITE_VISIBLE)] _IsSpriteVisible("Show Image", Float) = 1
// Internally enforced by MAX_RADIUS
_Radius("Blur Radius", Range(0, 64)) = 1
_OverlayColor("Blurred Overlay/Opacity", Color) = (0.5, 0.5, 0.5, 1)
// see Stencil in UI/Default
[HideInInspector][PerRendererData] _MainTex ("Sprite Texture", 2D) = "white" {}
[HideInInspector]_StencilComp ("Stencil Comparison", Float) = 8
[HideInInspector]_Stencil ("Stencil ID", Float) = 0
[HideInInspector]_StencilOp ("Stencil Operation", Float) = 0
[HideInInspector]_StencilWriteMask ("Stencil Write Mask", Float) = 255
[HideInInspector]_StencilReadMask ("Stencil Read Mask", Float) = 255
[HideInInspector]_ColorMask ("Color Mask", Float) = 15
[HideInInspector]_UseUIAlphaClip ("Use Alpha Clip", Float) = 0
}
Category
{
Tags
{
"Queue" = "Transparent"
"IgnoreProjector" = "True"
"RenderType" = "Transparent"
"PreviewType" = "Plane"
"CanUseSpriteAtlas" = "True"
}
Stencil
{
Ref [_Stencil]
Comp [_StencilComp]
Pass [_StencilOp]
ReadMask [_StencilReadMask]
WriteMask [_StencilWriteMask]
}
Cull Off
Lighting Off
ZWrite Off
ZTest [unity_GUIZTestMode]
Blend SrcAlpha OneMinusSrcAlpha
ColorMask [_ColorMask]
SubShader
{
GrabPass
{
Tags
{
"LightMode" = "Always"
"Queue" = "Background"
}
}
Pass
{
Name "UIBlur_Y"
Tags{ "LightMode" = "Always" }
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma fragmentoption ARB_precision_hint_fastest
#pragma multi_compile __ IS_BLUR_ALPHA_MASKED
#pragma multi_compile __ IS_SPRITE_VISIBLE
#pragma multi_compile __ UNITY_UI_ALPHACLIP
#include "UIBlur_Shared.cginc"
sampler2D _GrabTexture;
float4 _GrabTexture_TexelSize;
half4 frag(v2f IN) : COLOR
{
half4 pixel_raw = tex2D(_MainTex, IN.uvmain);
return GetBlurInDir(IN, pixel_raw, _GrabTexture, _GrabTexture_TexelSize, 0, 1);
}
ENDCG
}
GrabPass
{
Tags
{
"LightMode" = "Always"
"Queue" = "Background"
}
}
Pass
{
Name "UIBlur_X"
Tags{ "LightMode" = "Always" }
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma fragmentoption ARB_precision_hint_fastest
#pragma multi_compile __ IS_BLUR_ALPHA_MASKED
#pragma multi_compile __ IS_SPRITE_VISIBLE
#pragma multi_compile __ UNITY_UI_ALPHACLIP
#include "UIBlur_Shared.cginc"
sampler2D _GrabTexture;
float4 _GrabTexture_TexelSize;
half4 frag(v2f IN) : COLOR
{
half4 pixel_raw = tex2D(_MainTex, IN.uvmain);
#if IS_SPRITE_VISIBLE
return layerBlend(
// Layer 0 : The blurred background
GetBlurInDir(IN, pixel_raw, _GrabTexture, _GrabTexture_TexelSize, 1, 0),
// Layer 1 : The sprite itself
pixel_raw * IN.color
);
#else
return GetBlurInDir(IN, pixel_raw, _GrabTexture, _GrabTexture_TexelSize, 1, 0);
#endif
}
ENDCG
}
}
}
Fallback "UI/Default"
}
static const float MAX_RADIUS = 64;
static const float ITER_STEP = 2;
#include "UnityCG.cginc"
#include "UnityUI.cginc"
struct appdata_t
{
float4 vertex : POSITION;
float2 texcoord: TEXCOORD0;
float4 color : COLOR;
};
struct v2f
{
float4 vertex : POSITION;
float4 uvgrab : TEXCOORD0;
float4 worldpos : TEXCOORD1;
float2 uvmain : TEXCOORD2;
float4 color : COLOR;
};
sampler2D _MainTex;
float4 _MainTex_ST;
v2f vert(appdata_t v)
{
v2f OUT;
UNITY_SETUP_INSTANCE_ID(v);
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(OUT);
OUT.worldpos = v.vertex;
OUT.vertex = UnityObjectToClipPos(v.vertex);
#if UNITY_UV_STARTS_AT_TOP
float scale = -1.0;
#else
float scale = 1.0;
#endif
OUT.uvgrab.xy = (float2(OUT.vertex.x, OUT.vertex.y*scale) + OUT.vertex.w) * 0.5;
OUT.uvgrab.zw = OUT.vertex.zw;
OUT.uvmain = TRANSFORM_TEX(v.texcoord, _MainTex);
OUT.color = v.color;
return OUT;
}
float4 _OverlayColor;
float _Radius;
float4 _ClipRect;
half4 layerBlend(half4 back, half4 front)
{
half a0 = front.a;
half a1 = back.a;
half a01 = (1 - a0)*a1 + a0;
return half4(
((1 - a0)*a1*back.r + a0*front.r) / a01,
((1 - a0)*a1*back.g + a0*front.g) / a01,
((1 - a0)*a1*back.b + a0*front.b) / a01,
a01);
}
// Like Photoshop, see http://www.deepskycolors.com/archive/2010/04/21/formulas-for-Photoshop-blending-modes.html
#define BLEND_OVERLAY(a, b) b <= 0.5 ? (2*b)*a : (1 - (1-2*(b-0.5)) * (1-a))
half3 overlayBlend(half3 back, half3 front)
{
return
half3(
BLEND_OVERLAY(back.r, front.r),
BLEND_OVERLAY(back.g, front.g),
BLEND_OVERLAY(back.b, front.b)
);
}
half4 GrabPixel(sampler2D tex, float4 uv)
{
half4 pixel = tex2Dproj(tex, UNITY_PROJ_COORD(uv));
return half4(pixel.rgb, 1);
}
half4 GrabPixelXY(sampler2D tex, float4 uv, float4 size, half kernelx, half kernely)
{
half4 pixel = tex2Dproj(
tex,
UNITY_PROJ_COORD(
float4(
uv.x + size.x * kernelx,
uv.y + size.y * kernely,
uv.z,
uv.w)
)
);
return half4(pixel.rgb, 1);
}
half4 GetBlurInDir(v2f IN, half4 pixel, sampler2D tex, float4 size, half dirx, half diry)
{
#ifdef UNITY_COLORSPACE_GAMMA
float4 color = _OverlayColor;
#else
float4 color = float4(LinearToGammaSpace(_OverlayColor.rgb), _OverlayColor.a);
#endif
#if IS_BLUR_ALPHA_MASKED
float visibility = color.a*pixel.a;
#else
float visibility = color.a;
#endif
float radius = clamp(_Radius, 0, MAX_RADIUS);
visibility *= UnityGet2DClipping(IN.worldpos.xy, _ClipRect);
float4 sum = GrabPixel(tex, IN.uvgrab);
half steps = 1;
for (half range = ITER_STEP; range <= radius; range += ITER_STEP)
{
sum += GrabPixelXY(tex, IN.uvgrab, size, range*dirx, range*diry);
sum += GrabPixelXY(tex, IN.uvgrab, size, -range*dirx, -range*diry);
steps += 2;
}
half4 result = sum/steps;
return half4(overlayBlend(result.rgb, color.rgb), result.a*visibility);
}
@JohannesMP
Copy link
Author

This was developed on PC and I have not tested other platforms. It is shared here mainly as reference for anyone else writing their own shaders

@archimede67
Copy link

Does this work with URP ?

@JohannesMP
Copy link
Author

Does this work with URP ?

I have not tested it, I don’t use URP as part of my workflow yet.

In a topic on the Unity forums where I shared this originally, someone mentioned that they made a version based on mine that worked with URP: https://forum.unity.com/threads/solved-dynamic-blurred-background-on-ui.345083/page-2#post-6281381

@sebastiankmilo
Copy link

if i switch my canvas from Screen Space - Camera to Screen Space - Overlay it starts to work.

I cannot do that in my app, as there are few requirements which need the canvas to be in that mode, but I am trying to find out why it will not work.

@Theromanek100
Copy link

Hi, I've been using your shader for my project and I've got two reports about a strange bug with UI (https://gyazo.com/09493cad4ff14d28a42008e563042108). It doesn't happen on any of my devices so it's hard for me to find the problem. I know nothing about shaders, but maybe you can help identify the problem or a least exclude your shader from causing it? The person who sent me this screenshot has RTX 2060 Super, so I doubt that's a hardware incompatibility problem.

@Chuniqus
Copy link

Chuniqus commented Aug 5, 2022

Hello. Shader works great!. Can the code be used freely in commercial projects?

@JohannesMP
Copy link
Author

JohannesMP commented Aug 5, 2022

Hello. Shader works great!. Can the code be used freely in commercial projects?

Please absolutely feel free! If you do end up using it I’d love to know :)

As far as Licensing is concerned I am releasing this under the MIT License, so you are allowed to do what you wish but I also assume no responsibility in case (however unlikely) that it breaks anything. You are using code from the internet, so please exercise due diligence :)

If you can credit me, great, but I don’t require it.

Lastly I would still encourage you to write your own version to suit your specific needs. This has not been optimized or tested on various hardware configurations, and is primarily intended as a proof of concept to use as a starting point for anyone wishing to implement a similar effect.

@Chuniqus
Copy link

Chuniqus commented Aug 5, 2022

I'll gladly credit you. By the looks of it you can expect to be credited on obscure game on steam, probably around q3 of 2049 or so :)). I promise i'll let you know !

As of rewriting it, sadly not everyone is born equal, i'm untouched by ainur, and there is no shader magic running through my veins ^^.

@Jaakk0S
Copy link

Jaakk0S commented Dec 1, 2022

@JohannesMP Thank you for the shader. I tried yours and another free online solution (that I got working in the first place), and yours produces a better looking blur effect.

@Alvarden15
Copy link

Hello there. Just a few questions regarding this shader:

  • Is it performance friendy? As in: does it work well in mobile?
  • Does it work with Screen Space - Camera?
  • Does it work with 2022 LTS?

@roOHOH
Copy link

roOHOH commented Jul 11, 2023

Really awesome, helps me a lot
I think it is useful to build something like the floaters in eyes

@Silimon4ikGitHub
Copy link

You have saved my hope that chatGPT not so intelligent than people) thank you)

@RuudvanReenen
Copy link

Great, it works on World Space canvas as well, thanks a lot!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment