Search code examples
c#user-interfaceunity-game-engineuiblureffect

Unity3D : Blur the background of a UI canvas


I'm trying to create a blur effect on the background of a UI window for my game in Unity3D.

One of the best examples I can think of right now is Heroes Of The Storm, notice how the background of the Level Up panel blurs what's behind it :

Heroes Of The Storm Blur Effect

Is there any way to reproduce the same effect with the recently-added Canvas in Unity3D? I know there is a way to do so by using a camera, but I'm not really familiar with the whole thing, especially when it comes to making it work with the new UI system.

Thanks.


Solution

  • Here's a shader script that works nicely for me.

    Source: https://forum.unity3d.com/threads/solved-dynamic-blurred-background-on-ui.345083/#post-2853442

    // Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'
    
    Shader "Unlit/FrostedGlass"
    {
        Properties
        {
            _Radius("Radius", Range(1, 255)) = 1
        }
    
        Category
        {
            Tags{ "Queue" = "Transparent" "IgnoreProjector" = "True" "RenderType" = "Opaque" }
    
            SubShader
            {
                GrabPass
                {
                    Tags{ "LightMode" = "Always" }
                }
    
                Pass
                {
                    Tags{ "LightMode" = "Always" }
    
                    CGPROGRAM
                    #pragma vertex vert
                    #pragma fragment frag
                    #pragma fragmentoption ARB_precision_hint_fastest
                    #include "UnityCG.cginc"
    
                    struct appdata_t
                    {
                        float4 vertex : POSITION;
                        float2 texcoord: TEXCOORD0;
                    };
    
                    struct v2f
                    {
                        float4 vertex : POSITION;
                        float4 uvgrab : TEXCOORD0;
                    };
    
                    v2f vert(appdata_t v)
                    {
                        v2f o;
                        o.vertex = UnityObjectToClipPos(v.vertex);
                        #if UNITY_UV_STARTS_AT_TOP
                        float scale = -1.0;
                        #else
                        float scale = 1.0;
                        #endif
                        o.uvgrab.xy = (float2(o.vertex.x, o.vertex.y*scale) + o.vertex.w) * 0.5;
                        o.uvgrab.zw = o.vertex.zw;
                        return o;
                    }
    
                    sampler2D _GrabTexture;
                    float4 _GrabTexture_TexelSize;
                    float _Radius;
    
                    half4 frag(v2f i) : COLOR
                    {
                        half4 sum = half4(0,0,0,0);
    
                        #define GRABXYPIXEL(kernelx, kernely) tex2Dproj( _GrabTexture, UNITY_PROJ_COORD(float4(i.uvgrab.x + _GrabTexture_TexelSize.x * kernelx, i.uvgrab.y + _GrabTexture_TexelSize.y * kernely, i.uvgrab.z, i.uvgrab.w)))
    
                        sum += GRABXYPIXEL(0.0, 0.0);
                        int measurments = 1;
    
                        for (float range = 0.1f; range <= _Radius; range += 0.1f)
                        {
                            sum += GRABXYPIXEL(range, range);
                            sum += GRABXYPIXEL(range, -range);
                            sum += GRABXYPIXEL(-range, range);
                            sum += GRABXYPIXEL(-range, -range);
                            measurments += 4;
                        }
    
                        return sum / measurments;
                    }
                    ENDCG
                }
                GrabPass
                {
                    Tags{ "LightMode" = "Always" }
                }
    
                Pass
                {
                    Tags{ "LightMode" = "Always" }
    
                    CGPROGRAM
                    #pragma vertex vert
                    #pragma fragment frag
                    #pragma fragmentoption ARB_precision_hint_fastest
                    #include "UnityCG.cginc"
    
                    struct appdata_t
                    {
                        float4 vertex : POSITION;
                        float2 texcoord: TEXCOORD0;
                    };
    
                    struct v2f
                    {
                        float4 vertex : POSITION;
                        float4 uvgrab : TEXCOORD0;
                    };
    
                    v2f vert(appdata_t v)
                    {
                        v2f o;
                        o.vertex = UnityObjectToClipPos(v.vertex);
                        #if UNITY_UV_STARTS_AT_TOP
                        float scale = -1.0;
                        #else
                        float scale = 1.0;
                        #endif
                        o.uvgrab.xy = (float2(o.vertex.x, o.vertex.y*scale) + o.vertex.w) * 0.5;
                        o.uvgrab.zw = o.vertex.zw;
                        return o;
                    }
    
                    sampler2D _GrabTexture;
                    float4 _GrabTexture_TexelSize;
                    float _Radius;
    
                    half4 frag(v2f i) : COLOR
                    {
    
                        half4 sum = half4(0,0,0,0);
                        float radius = 1.41421356237 * _Radius;
    
                        #define GRABXYPIXEL(kernelx, kernely) tex2Dproj( _GrabTexture, UNITY_PROJ_COORD(float4(i.uvgrab.x + _GrabTexture_TexelSize.x * kernelx, i.uvgrab.y + _GrabTexture_TexelSize.y * kernely, i.uvgrab.z, i.uvgrab.w)))
    
                        sum += GRABXYPIXEL(0.0, 0.0);
                        int measurments = 1;
    
                        for (float range = 1.41421356237f; range <= radius * 1.41; range += 1.41421356237f)
                        {
                            sum += GRABXYPIXEL(range, 0);
                            sum += GRABXYPIXEL(-range, 0);
                            sum += GRABXYPIXEL(0, range);
                            sum += GRABXYPIXEL(0, -range);
                            measurments += 4;
                        }
    
                        return sum / measurments;
                    }
                    ENDCG
                }
            }
        }
    }