Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Simple Optimized Blur Shader

Discussion in 'Shaders' started by MaT227, Jun 7, 2013.

  1. MaT227

    MaT227

    Joined:
    Jul 3, 2012
    Posts:
    628
    Hi,

    I have a vertex-fragment shader that use the Unity3D GrabPass functionality (it grabs the screen). And I apply my GrabPass to have a transparent effect.


    Code (csharp):
    1.  GrabPass {
    2.         "_GrabTex"
    3.     }
    4.    
    5.     sampler2D   _GrabTex;
    6.    
    7.     // float4 grabPassPos : TEXCOORD4 is used to apply the grabbed texture
    8.     // in the right place and is calculated in the vertex
    9.     half4 transparent = tex2Dproj(_GrabTex, UNITY_PROJ_COORD(IN.grabPassPos));
    10.     half4 baseColor = transparent;
    11.     return baseColor;
    12.  
    I am searching a simple way of blurring my grabbed texture. I don't know how to apply my blur because it's a tex2Dproj and I don't know where to apply it.
    I am searching for a one pass optimized simple blur but I don't know if it's possible.
    I know a bit about Gaussian and Box blur which seems to be the simplest but how can I apply them in a tex2Dproj ?

    I know this package but it seems to be controversial and not very optimized.

    Thanks a lot !
     
    Last edited: Jun 8, 2013
  2. aubergine

    aubergine

    Joined:
    Sep 12, 2009
    Posts:
    2,878
    Dont use grabpass, use a custom rendertexture and the simplest/lousiest way to blur it is to reduce the size of the texture after rendering to it.
    Or there are also alternative ways as you mentioned.
    The main point is, if you want control on things, then dont use grabpass.
     
  3. MaT227

    MaT227

    Joined:
    Jul 3, 2012
    Posts:
    628
    Thanks for your answer Aubergine !
    It is for a water transparency effect how can I use something other than GrabPass ? For me GrabPass is more optimized than a camera with a renderTexture but maybe I am wrong.
    I can't apply some blur on the grabbed texture ?
     
  4. aubergine

    aubergine

    Joined:
    Sep 12, 2009
    Posts:
    2,878
    grabpass is a render texture itself that is rendered internally. You can do the blur with grabpass too, but if you want more control, render it yourself.
     
  5. MaT227

    MaT227

    Joined:
    Jul 3, 2012
    Posts:
    628
    For the moment I want to apply blur on my grabpass texture.
    I saw that I can do it like this :

    Code (csharp):
    1. float3 proj = UNITY_PROJ_COORD(grabWithOffset);
    2. half4 top = tex2Dproj(_GrabTex, proj + float3(0, -_Blur, 0));
    3. half4 bot = tex2Dproj(_GrabTex, proj + float3(0, _Blur, 0));
    4. half4 left = tex2Dproj(_GrabTex, proj + float3(-_Blur, 0, 0));
    5. half4 right = tex2Dproj(_GrabTex, proj + float3(_Blur,0, 0));
    6. half4 center = tex2Dproj(_GrabTex, proj );         
    7. return ((top + bot + left + right + center + center) / 6.0f);
    But this is hardcoded. And the result is a very simple blur not smoothed.

    I know that there is a concept of kernel but I don't understand it. If you set the value in the kernel which seems to be an array how can you apply it in your texture.
    And I saw that this kernel can be used in a loop, but in unity shader there is no access to for or while loops.
     
    JasonSpine likes this.
  6. cician

    cician

    Joined:
    Dec 10, 2012
    Posts:
    233
    Grabpass is different from a rendertexture in that it doesn't require to re-render the scene. It blits the contents of the framebuffer to a temporary texture, which isn't free but still much cheaper than rendering the scene multiple times.
    Sure, you have more control with render to texture approaches, but for simple stuff grabpass should be fine.

    I slapped toghether the refraction shader with gaussian blur. It's a bit hackish, but gets the job done.

    The kernel is hard coded for efficiency. If you know an efficient way for a dynamic kernel, let me know ;)
    Depending on geometry it could be more efficient to do a single pass with two dimensional gaussian, at least with a small kernel. Not sure though. I did it splitted in horizontal+vertical as everyone else and I'm too lazy to try.
    Third pass is just for distortion and tint color.

    Code (csharp):
    1. Shader "Custom/SimpleGrabPassBlur" {
    2.     Properties {
    3.         _Color ("Main Color", Color) = (1,1,1,1)
    4.         _BumpAmt  ("Distortion", Range (0,128)) = 10
    5.         _MainTex ("Tint Color (RGB)", 2D) = "white" {}
    6.         _BumpMap ("Normalmap", 2D) = "bump" {}
    7.         _Size ("Size", Range(0, 20)) = 1
    8.     }
    9.    
    10.     Category {
    11.    
    12.         // We must be transparent, so other objects are drawn before this one.
    13.         Tags { "Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Opaque" }
    14.    
    15.    
    16.         SubShader {
    17.        
    18.             // Horizontal blur
    19.             GrabPass {                     
    20.                 Tags { "LightMode" = "Always" }
    21.             }
    22.             Pass {
    23.                 Tags { "LightMode" = "Always" }
    24.                
    25.                 CGPROGRAM
    26.                 #pragma vertex vert
    27.                 #pragma fragment frag
    28.                 #pragma fragmentoption ARB_precision_hint_fastest
    29.                 #include "UnityCG.cginc"
    30.                
    31.                 struct appdata_t {
    32.                     float4 vertex : POSITION;
    33.                     float2 texcoord: TEXCOORD0;
    34.                 };
    35.                
    36.                 struct v2f {
    37.                     float4 vertex : POSITION;
    38.                     float4 uvgrab : TEXCOORD0;
    39.                 };
    40.                
    41.                 v2f vert (appdata_t v) {
    42.                     v2f o;
    43.                     o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
    44.                     #if UNITY_UV_STARTS_AT_TOP
    45.                     float scale = -1.0;
    46.                     #else
    47.                     float scale = 1.0;
    48.                     #endif
    49.                     o.uvgrab.xy = (float2(o.vertex.x, o.vertex.y*scale) + o.vertex.w) * 0.5;
    50.                     o.uvgrab.zw = o.vertex.zw;
    51.                     return o;
    52.                 }
    53.                
    54.                 sampler2D _GrabTexture;
    55.                 float4 _GrabTexture_TexelSize;
    56.                 float _Size;
    57.                
    58.                 half4 frag( v2f i ) : COLOR {
    59. //                  half4 col = tex2Dproj( _GrabTexture, UNITY_PROJ_COORD(i.uvgrab));
    60. //                  return col;
    61.                    
    62.                     half4 sum = half4(0,0,0,0);
    63.  
    64.                     #define GRABPIXEL(weight,kernelx) tex2Dproj( _GrabTexture, UNITY_PROJ_COORD(float4(i.uvgrab.x + _GrabTexture_TexelSize.x * kernelx*_Size, i.uvgrab.y, i.uvgrab.z, i.uvgrab.w))) * weight
    65.  
    66.                     sum += GRABPIXEL(0.05, -4.0);
    67.                     sum += GRABPIXEL(0.09, -3.0);
    68.                     sum += GRABPIXEL(0.12, -2.0);
    69.                     sum += GRABPIXEL(0.15, -1.0);
    70.                     sum += GRABPIXEL(0.18,  0.0);
    71.                     sum += GRABPIXEL(0.15, +1.0);
    72.                     sum += GRABPIXEL(0.12, +2.0);
    73.                     sum += GRABPIXEL(0.09, +3.0);
    74.                     sum += GRABPIXEL(0.05, +4.0);
    75.                    
    76.                     return sum;
    77.                 }
    78.                 ENDCG
    79.             }
    80.  
    81.             // Vertical blur
    82.             GrabPass {                         
    83.                 Tags { "LightMode" = "Always" }
    84.             }
    85.             Pass {
    86.                 Tags { "LightMode" = "Always" }
    87.                
    88.                 CGPROGRAM
    89.                 #pragma vertex vert
    90.                 #pragma fragment frag
    91.                 #pragma fragmentoption ARB_precision_hint_fastest
    92.                 #include "UnityCG.cginc"
    93.                
    94.                 struct appdata_t {
    95.                     float4 vertex : POSITION;
    96.                     float2 texcoord: TEXCOORD0;
    97.                 };
    98.                
    99.                 struct v2f {
    100.                     float4 vertex : POSITION;
    101.                     float4 uvgrab : TEXCOORD0;
    102.                 };
    103.                
    104.                 v2f vert (appdata_t v) {
    105.                     v2f o;
    106.                     o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
    107.                     #if UNITY_UV_STARTS_AT_TOP
    108.                     float scale = -1.0;
    109.                     #else
    110.                     float scale = 1.0;
    111.                     #endif
    112.                     o.uvgrab.xy = (float2(o.vertex.x, o.vertex.y*scale) + o.vertex.w) * 0.5;
    113.                     o.uvgrab.zw = o.vertex.zw;
    114.                     return o;
    115.                 }
    116.                
    117.                 sampler2D _GrabTexture;
    118.                 float4 _GrabTexture_TexelSize;
    119.                 float _Size;
    120.                
    121.                 half4 frag( v2f i ) : COLOR {
    122. //                  half4 col = tex2Dproj( _GrabTexture, UNITY_PROJ_COORD(i.uvgrab));
    123. //                  return col;
    124.                    
    125.                     half4 sum = half4(0,0,0,0);
    126.  
    127.                     #define GRABPIXEL(weight,kernely) tex2Dproj( _GrabTexture, UNITY_PROJ_COORD(float4(i.uvgrab.x, i.uvgrab.y + _GrabTexture_TexelSize.y * kernely*_Size, i.uvgrab.z, i.uvgrab.w))) * weight
    128.  
    129.                     //G(X) = (1/(sqrt(2*PI*deviation*deviation))) * exp(-(x*x / (2*deviation*deviation)))
    130.                    
    131.                     sum += GRABPIXEL(0.05, -4.0);
    132.                     sum += GRABPIXEL(0.09, -3.0);
    133.                     sum += GRABPIXEL(0.12, -2.0);
    134.                     sum += GRABPIXEL(0.15, -1.0);
    135.                     sum += GRABPIXEL(0.18,  0.0);
    136.                     sum += GRABPIXEL(0.15, +1.0);
    137.                     sum += GRABPIXEL(0.12, +2.0);
    138.                     sum += GRABPIXEL(0.09, +3.0);
    139.                     sum += GRABPIXEL(0.05, +4.0);
    140.                    
    141.                     return sum;
    142.                 }
    143.                 ENDCG
    144.             }
    145.            
    146.             // Distortion
    147.             GrabPass {                         
    148.                 Tags { "LightMode" = "Always" }
    149.             }
    150.             Pass {
    151.                 Tags { "LightMode" = "Always" }
    152.                
    153.                 CGPROGRAM
    154.                 #pragma vertex vert
    155.                 #pragma fragment frag
    156.                 #pragma fragmentoption ARB_precision_hint_fastest
    157.                 #include "UnityCG.cginc"
    158.                
    159.                 struct appdata_t {
    160.                     float4 vertex : POSITION;
    161.                     float2 texcoord: TEXCOORD0;
    162.                 };
    163.                
    164.                 struct v2f {
    165.                     float4 vertex : POSITION;
    166.                     float4 uvgrab : TEXCOORD0;
    167.                     float2 uvbump : TEXCOORD1;
    168.                     float2 uvmain : TEXCOORD2;
    169.                 };
    170.                
    171.                 float _BumpAmt;
    172.                 float4 _BumpMap_ST;
    173.                 float4 _MainTex_ST;
    174.                
    175.                 v2f vert (appdata_t v) {
    176.                     v2f o;
    177.                     o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
    178.                     #if UNITY_UV_STARTS_AT_TOP
    179.                     float scale = -1.0;
    180.                     #else
    181.                     float scale = 1.0;
    182.                     #endif
    183.                     o.uvgrab.xy = (float2(o.vertex.x, o.vertex.y*scale) + o.vertex.w) * 0.5;
    184.                     o.uvgrab.zw = o.vertex.zw;
    185.                     o.uvbump = TRANSFORM_TEX( v.texcoord, _BumpMap );
    186.                     o.uvmain = TRANSFORM_TEX( v.texcoord, _MainTex );
    187.                     return o;
    188.                 }
    189.                
    190.                 fixed4 _Color;
    191.                 sampler2D _GrabTexture;
    192.                 float4 _GrabTexture_TexelSize;
    193.                 sampler2D _BumpMap;
    194.                 sampler2D _MainTex;
    195.                
    196.                 half4 frag( v2f i ) : COLOR {
    197.                     // calculate perturbed coordinates
    198.                     half2 bump = UnpackNormal(tex2D( _BumpMap, i.uvbump )).rg; // we could optimize this by just reading the x  y without reconstructing the Z
    199.                     float2 offset = bump * _BumpAmt * _GrabTexture_TexelSize.xy;
    200.                     i.uvgrab.xy = offset * i.uvgrab.z + i.uvgrab.xy;
    201.                    
    202.                     half4 col = tex2Dproj( _GrabTexture, UNITY_PROJ_COORD(i.uvgrab));
    203.                     half4 tint = tex2D( _MainTex, i.uvmain ) * _Color;
    204.                    
    205.                     return col * tint;
    206.                 }
    207.                 ENDCG
    208.             }
    209.         }
    210.     }
    211. }
    212.  
    Here's how it looks.
    $blur.jpg

    Cheers.
     
  7. MaT227

    MaT227

    Joined:
    Jul 3, 2012
    Posts:
    628
    Thanks a lot Cician ! I will look at your shader.

    I've found an interesting optimization of gaussian blur.
    But I don't know if it's really convincing.

    I don't understand why the number of pass should depend on the geometry.
    Why do separate the refraction pass ? I thought that the more pass you have the less it's optimized.
    What are the interest of having multiple pass (I know that it's of topic but I am just asking)

    A strange but normal behaviour appended on zoom, I can see the duplicate of the texture, is there a way to avoid that ?
     
    Last edited: Jun 8, 2013
  8. cician

    cician

    Joined:
    Dec 10, 2012
    Posts:
    233
    The shader in fact is not optimized at all.

    Most people optimize gaussian blur by splitting it into horizontal and vertical pass, but in this case mesh geometry is rendered for each pass, which may outweight the benefits depending on multiple factors, such as triangle count and kernel size.
    Its's first time I'm doing the blur thingy and I'm just guessing this is the case, so take with a grain of salt.

    As for diffraction pass, it's just simpler this way. In fact the second and third passes can be combined into one.

    I don't understand. Screenshot or it didn't happen.
    Just a guess: try Z-Priming.

    NB: usual gotchas apply...
    Transparency issues: popping and no shadows.
    Grabpass: foreground objects bleeding in the distortion (avoidable with a mask, see gpu gems) and artifacts near screen edges.
     
  9. MaT227

    MaT227

    Joined:
    Jul 3, 2012
    Posts:
    628
    Here is a screenshot of what I think it's strange but "normal". When I zoom with the camera.

    $3.jpg

    Where does it come from ? For me it depends of the kernel size and the smooth of the blur.
     
    Last edited: Jun 8, 2013
  10. MaT227

    MaT227

    Joined:
    Jul 3, 2012
    Posts:
    628
    Is there a way to avoid this effect ?
    Is there a solution to have an efficient simple blur effect. It seems that every blur is based on the duplication of the image then blending.
     
  11. Annihlator

    Annihlator

    Joined:
    Oct 15, 2012
    Posts:
    378
    That's exactly how blur is done cheaply. by taking a picture, blowing it up and showing it again.
    More extensive forms of blur would become much more heavy (And i wouldn't be sure how that's computed either).
     
  12. SomeRandomGuy

    SomeRandomGuy

    Joined:
    Jul 4, 2012
    Posts:
    11
    I can't seem to get that example to work, throws a ton of errors. something about the shader not having certain properties, while those properties certainly seem to be there. I am still very much learning how shaders work, so it all seems very complex to me. I was having a look at the outline toon shader in unity to make something like a glow shader, but I can't seem to figure out how I'd blur the outline =/
    Maybe that shader is not the best thing to use as a base for it.
     
  13. jack_cohen

    jack_cohen

    Joined:
    Feb 11, 2014
    Posts:
    1
    seems not work on mobile
     
  14. flyingbanana

    flyingbanana

    Joined:
    Jan 28, 2014
    Posts:
    22
    This doesn't work well on mobile but I managed to get rid of the artifacts up to ~30 pixel strength. The edges will suffer on that level of strength though.

    Gaussian Blur

    This is a 10 pixel (gap) Gaussian blur.

     
    Last edited: Aug 5, 2015
  15. cician

    cician

    Joined:
    Dec 10, 2012
    Posts:
    233
  16. Pratap-Dafedar

    Pratap-Dafedar

    Joined:
    Aug 30, 2013
    Posts:
    22
    Hi Cician,
    Have you updated your shader using the above approach Unity's new CB. Please post your new shader here, if So..
     
  17. cician

    cician

    Joined:
    Dec 10, 2012
    Posts:
    233
    No. Sorry, but I'm currently mostly working in Unreal Engine. I did try the provided example though. It works (albeit not correctly in editor viewport, correctly at runtime) and it uses a separable blur. Didn't try anything beyond that.
     
  18. Pratap-Dafedar

    Pratap-Dafedar

    Joined:
    Aug 30, 2013
    Posts:
    22
    Yup, I tried this too. But, It is not performance friendly... :(:(
    Any way, Thank you Cician for sharing the shader here, I really appreciate that.. :rolleyes::rolleyes:
     
  19. deekpyro

    deekpyro

    Joined:
    Oct 16, 2012
    Posts:
    71
    Reviving this thread as I'm trying to do small blurs like @flyingbanana using the Command Buffers approach example Aras posted. Unfortunately even on the latest iOS devices and rendering a small portion of the screen the framerate plummets.

    Has anyone had success during these sorts of blurs with Command Buffers on mobile?
     
  20. hippocoder

    hippocoder

    Digital Ape

    Joined:
    Apr 11, 2010
    Posts:
    29,723
    I think deferred would be your problem on mobile long before command buffers got involved... then there's grabpass and all sorts of expensive stuff.
     
  21. deekpyro

    deekpyro

    Joined:
    Oct 16, 2012
    Posts:
    71
    Command Buffers work with Forward Rendering too.
     
  22. cician

    cician

    Joined:
    Dec 10, 2012
    Posts:
    233
    I don't have much experience with mobile, but here's some food for thought.
    Fist I'd try what is the performance hit of just capturing the framebuffer or a part of it and redrawing it with a simple shader. If that's acceptably low then you can try optimizing the blur part.
    If you target modern devices with compute support then I suggest you read this: https://software.intel.com/en-us/bl...ast-real-time-gpu-based-image-blur-algorithms
    Otherwise you could maybe take advantage of the tiling nature of [most] mobile GPUs.
    Even without compute support the above article gives some insights. If you cannot afford a full gaussian blur, you can cheat with cheap[er] box blurs and find a performance-quality sweet spot.
     
    brokenm likes this.
  23. Untherow

    Untherow

    Joined:
    Jun 19, 2016
    Posts:
    8
    That is amazing! Just what I needed thank you!!
     
  24. vinipc

    vinipc

    Joined:
    Jun 21, 2013
    Posts:
    8
    So, I know this is an old thread, but I just needed some UI Blur for a project and cician's shader helped a lot. However, I ended up modifying it a bit so I can use the Image's alpha to mask the blur, so I'll just post the shader here in case anyone needs it in the future.

    Two notes however:
    1) Because of the grab passes it is pretty much unusable on iOS (and maybe Android as well. I haven't tested).
    2) I made to use with Unity's UI Image component. If you need it for something else, you can probably remove the [HideInInspector] property from the _MainTex.

    Code (CSharp):
    1. // Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'
    2.  
    3. // Based on cician's shader from: https://forum.unity3d.com/threads/simple-optimized-blur-shader.185327/#post-1267642
    4.  
    5. Shader "Custom/MaskedUIBlur" {
    6.     Properties {
    7.         _Size ("Blur", Range(0, 30)) = 1
    8.         [HideInInspector] _MainTex ("Tint Color (RGB)", 2D) = "white" {}
    9.     }
    10.     Category {
    11.         // We must be transparent, so other objects are drawn before this one.
    12.         Tags { "Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Opaque" }
    13.         SubShader
    14.         {
    15.             // Horizontal blur
    16.             GrabPass
    17.             {
    18.                 "_HBlur"
    19.             }
    20.  
    21.             Pass
    22.             {            
    23.                 CGPROGRAM
    24.                 #pragma vertex vert
    25.                 #pragma fragment frag
    26.                 #pragma fragmentoption ARB_precision_hint_fastest
    27.                 #include "UnityCG.cginc"
    28.            
    29.                 struct appdata_t {
    30.                     float4 vertex : POSITION;
    31.                     float2 texcoord: TEXCOORD0;
    32.                 };
    33.            
    34.                 struct v2f {
    35.                     float4 vertex : POSITION;
    36.                     float4 uvgrab : TEXCOORD0;
    37.                     float2 uvmain : TEXCOORD1;
    38.                 };
    39.  
    40.                 sampler2D _MainTex;
    41.                 float4 _MainTex_ST;
    42.  
    43.                 v2f vert (appdata_t v)
    44.                 {
    45.                     v2f o;
    46.                     o.vertex = UnityObjectToClipPos(v.vertex);
    47.  
    48.                     #if UNITY_UV_STARTS_AT_TOP
    49.                     float scale = -1.0;
    50.                     #else
    51.                     float scale = 1.0;
    52.                     #endif
    53.  
    54.                     o.uvgrab.xy = (float2(o.vertex.x, o.vertex.y * scale) + o.vertex.w) * 0.5;
    55.                     o.uvgrab.zw = o.vertex.zw;
    56.  
    57.                     o.uvmain = TRANSFORM_TEX(v.texcoord, _MainTex);
    58.                     return o;
    59.                 }
    60.            
    61.                 sampler2D _HBlur;
    62.                 float4 _HBlur_TexelSize;
    63.                 float _Size;
    64.  
    65.                 half4 frag( v2f i ) : COLOR
    66.                 {    
    67.                     float alpha = tex2D(_MainTex, i.uvmain).a;
    68.                     half4 sum = half4(0,0,0,0);
    69.  
    70.                     #define GRABPIXEL(weight,kernelx) tex2Dproj( _HBlur, UNITY_PROJ_COORD(float4(i.uvgrab.x + _HBlur_TexelSize.x * kernelx * _Size * alpha, i.uvgrab.y, i.uvgrab.z, i.uvgrab.w))) * weight
    71.  
    72.                     sum += GRABPIXEL(0.05, -4.0);
    73.                     sum += GRABPIXEL(0.09, -3.0);
    74.                     sum += GRABPIXEL(0.12, -2.0);
    75.                     sum += GRABPIXEL(0.15, -1.0);
    76.                     sum += GRABPIXEL(0.18,  0.0);
    77.                     sum += GRABPIXEL(0.15, +1.0);
    78.                     sum += GRABPIXEL(0.12, +2.0);
    79.                     sum += GRABPIXEL(0.09, +3.0);
    80.                     sum += GRABPIXEL(0.05, +4.0);
    81.  
    82.                     return sum ;
    83.                 }
    84.                 ENDCG
    85.             }
    86.  
    87.             // Vertical blur
    88.             GrabPass
    89.             {
    90.                 "_VBlur"
    91.             }
    92.  
    93.             Pass
    94.             {            
    95.                 CGPROGRAM
    96.                 #pragma vertex vert
    97.                 #pragma fragment frag
    98.                 #pragma fragmentoption ARB_precision_hint_fastest
    99.                 #include "UnityCG.cginc"
    100.            
    101.                 struct appdata_t {
    102.                     float4 vertex : POSITION;
    103.                     float2 texcoord: TEXCOORD0;
    104.                 };
    105.            
    106.                 struct v2f {
    107.                     float4 vertex : POSITION;
    108.                     float4 uvgrab : TEXCOORD0;
    109.                     float2 uvmain : TEXCOORD1;
    110.                 };
    111.  
    112.                 sampler2D _MainTex;
    113.                 float4 _MainTex_ST;
    114.  
    115.                 v2f vert (appdata_t v) {
    116.                     v2f o;
    117.                     o.vertex = UnityObjectToClipPos(v.vertex);
    118.  
    119.                     #if UNITY_UV_STARTS_AT_TOP
    120.                     float scale = -1.0;
    121.                     #else
    122.                     float scale = 1.0;
    123.                     #endif
    124.  
    125.                     o.uvgrab.xy = (float2(o.vertex.x, o.vertex.y * scale) + o.vertex.w) * 0.5;
    126.                     o.uvgrab.zw = o.vertex.zw;
    127.  
    128.                     o.uvmain = TRANSFORM_TEX(v.texcoord, _MainTex);
    129.  
    130.                     return o;
    131.                 }
    132.            
    133.                 sampler2D _VBlur;
    134.                 float4 _VBlur_TexelSize;
    135.                 float _Size;
    136.            
    137.                 half4 frag( v2f i ) : COLOR
    138.                 {
    139.                     float alpha = tex2D(_MainTex, i.uvmain).a;
    140.                     half4 sum = half4(0,0,0,0);
    141.  
    142.                     #define GRABPIXEL(weight,kernely) tex2Dproj( _VBlur, UNITY_PROJ_COORD(float4(i.uvgrab.x, i.uvgrab.y + _VBlur_TexelSize.y * kernely * _Size * alpha, i.uvgrab.z, i.uvgrab.w))) * weight
    143.  
    144.                     sum += GRABPIXEL(0.05, -4.0);
    145.                     sum += GRABPIXEL(0.09, -3.0);
    146.                     sum += GRABPIXEL(0.12, -2.0);
    147.                     sum += GRABPIXEL(0.15, -1.0);
    148.                     sum += GRABPIXEL(0.18,  0.0);
    149.                     sum += GRABPIXEL(0.15, +1.0);
    150.                     sum += GRABPIXEL(0.12, +2.0);
    151.                     sum += GRABPIXEL(0.09, +3.0);
    152.                     sum += GRABPIXEL(0.05, +4.0);
    153.  
    154.                     return sum;
    155.                 }
    156.                 ENDCG
    157.             }
    158.         }
    159.     }
    160. }
     

    Attached Files:

  25. radiatoryang

    radiatoryang

    Joined:
    Jul 7, 2014
    Posts:
    19
    in case anyone comes across this, I modded vinipc's masked blur UI shader to add vertex color support, which means the UI Image's color property can affect the sprite now... here's my modified version:

    Code (CSharp):
    1.  
    2. // Based on cician's shader from: https://forum.unity3d.com/threads/simple-optimized-blur-shader.185327/#post-1267642
    3. // also from https://forum.unity3d.com/threads/simple-optimized-blur-shader.185327/
    4.  
    5. Shader "Custom/MaskedUIBlur" {
    6.     Properties {
    7.         _Size ("Blur", Range(0, 30)) = 1
    8.         [HideInInspector] _MainTex ("Texture (RGB)", 2D) = "white" {}
    9.         // _TintColor ("Tint Color", Color) = (1,1,1,1)
    10.     }
    11.     Category {
    12.         // We must be transparent, so other objects are drawn before this one.
    13.         Tags { "Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Opaque" }
    14.         SubShader
    15.         {
    16.             ZWrite Off
    17.             // Horizontal blur
    18.             GrabPass
    19.             {
    20.                 "_HBlur"
    21.             }
    22.  
    23.             Pass
    24.             {            
    25.                 CGPROGRAM
    26.                 #pragma vertex vert
    27.                 #pragma fragment frag
    28.                 #pragma fragmentoption ARB_precision_hint_fastest
    29.                 #include "UnityCG.cginc"
    30.            
    31.                 struct appdata_t {
    32.                     float4 vertex : POSITION;
    33.                     float2 texcoord: TEXCOORD0;
    34.                     float4 color    : COLOR;
    35.                 };
    36.            
    37.                 struct v2f {
    38.                     float4 vertex : POSITION;
    39.                     float4 uvgrab : TEXCOORD0;
    40.                     float2 uvmain : TEXCOORD1;
    41.                     float4 color    : COLOR;
    42.                 };
    43.  
    44.                 sampler2D _MainTex;
    45.                 float4 _MainTex_ST;
    46.  
    47.                 v2f vert (appdata_t v)
    48.                 {
    49.                     v2f o;
    50.                     o.vertex = UnityObjectToClipPos(v.vertex);
    51.  
    52.                     #if UNITY_UV_STARTS_AT_TOP
    53.                     float scale = -1.0;
    54.                     #else
    55.                     float scale = 1.0;
    56.                     #endif
    57.  
    58.                     o.uvgrab.xy = (float2(o.vertex.x, o.vertex.y * scale) + o.vertex.w) * 0.5;
    59.                     o.uvgrab.zw = o.vertex.zw;
    60.  
    61.                     o.uvmain = TRANSFORM_TEX(v.texcoord, _MainTex);
    62.                     o.color = v.color;
    63.                     return o;
    64.                 }
    65.            
    66.                 sampler2D _HBlur;
    67.                 float4 _HBlur_TexelSize;
    68.                 float _Size;
    69.  
    70.                 half4 frag( v2f i ) : COLOR
    71.                 {    
    72.                     float alpha = tex2D(_MainTex, i.uvmain).a * i.color.a;
    73.                     half4 sum = half4(0,0,0,0);
    74.  
    75.                     #define GRABPIXEL(weight,kernelx) tex2Dproj( _HBlur, UNITY_PROJ_COORD(float4(i.uvgrab.x + _HBlur_TexelSize.x * kernelx * _Size * alpha, i.uvgrab.y, i.uvgrab.z, i.uvgrab.w))) * weight
    76.  
    77.                     sum += GRABPIXEL(0.05, -4.0);
    78.                     sum += GRABPIXEL(0.09, -3.0);
    79.                     sum += GRABPIXEL(0.12, -2.0);
    80.                     sum += GRABPIXEL(0.15, -1.0);
    81.                     sum += GRABPIXEL(0.18,  0.0);
    82.                     sum += GRABPIXEL(0.15, +1.0);
    83.                     sum += GRABPIXEL(0.12, +2.0);
    84.                     sum += GRABPIXEL(0.09, +3.0);
    85.                     sum += GRABPIXEL(0.05, +4.0);
    86.  
    87.                     return sum ;
    88.                 }
    89.                 ENDCG
    90.             }
    91.  
    92.             // Vertical blur
    93.             GrabPass
    94.             {
    95.                 "_VBlur"
    96.             }
    97.  
    98.             Pass
    99.             {            
    100.                 CGPROGRAM
    101.                 #pragma vertex vert
    102.                 #pragma fragment frag
    103.                 #pragma fragmentoption ARB_precision_hint_fastest
    104.                 #include "UnityCG.cginc"
    105.            
    106.                 struct appdata_t {
    107.                     float4 vertex : POSITION;
    108.                     float2 texcoord: TEXCOORD0;
    109.                     float4 color    : COLOR;
    110.                 };
    111.            
    112.                 struct v2f {
    113.                     float4 vertex : POSITION;
    114.                     float4 uvgrab : TEXCOORD0;
    115.                     float2 uvmain : TEXCOORD1;
    116.                     float4 color    : COLOR;
    117.                 };
    118.  
    119.                 sampler2D _MainTex;
    120.                 float4 _MainTex_ST;
    121.  
    122.                 v2f vert (appdata_t v) {
    123.                     v2f o;
    124.                     o.vertex = UnityObjectToClipPos(v.vertex);
    125.  
    126.                     #if UNITY_UV_STARTS_AT_TOP
    127.                     float scale = -1.0;
    128.                     #else
    129.                     float scale = 1.0;
    130.                     #endif
    131.  
    132.                     o.uvgrab.xy = (float2(o.vertex.x, o.vertex.y * scale) + o.vertex.w) * 0.5;
    133.                     o.uvgrab.zw = o.vertex.zw;
    134.  
    135.                     o.uvmain = TRANSFORM_TEX(v.texcoord, _MainTex);
    136.                     o.color = v.color;
    137.                     return o;
    138.                 }
    139.            
    140.                 sampler2D _VBlur;
    141.                 float4 _VBlur_TexelSize;
    142.                 float _Size;
    143.                 //fixed4 _TintColor;
    144.            
    145.                 half4 frag( v2f i ) : COLOR
    146.                 {
    147.                     float alpha = tex2D(_MainTex, i.uvmain).a * i.color.a;
    148.                     half4 sum = half4(0,0,0,0);
    149.  
    150.                     #define GRABPIXEL(weight,kernely) tex2Dproj( _VBlur, UNITY_PROJ_COORD(float4(i.uvgrab.x, i.uvgrab.y + _VBlur_TexelSize.y * kernely * _Size * alpha, i.uvgrab.z, i.uvgrab.w))) * weight
    151.  
    152.                     sum += GRABPIXEL(0.05, -4.0);
    153.                     sum += GRABPIXEL(0.09, -3.0);
    154.                     sum += GRABPIXEL(0.12, -2.0);
    155.                     sum += GRABPIXEL(0.15, -1.0);
    156.                     sum += GRABPIXEL(0.18,  0.0);
    157.                     sum += GRABPIXEL(0.15, +1.0);
    158.                     sum += GRABPIXEL(0.12, +2.0);
    159.                     sum += GRABPIXEL(0.09, +3.0);
    160.                     sum += GRABPIXEL(0.05, +4.0);
    161.  
    162.                     return sum + i.color * alpha;
    163.                 }
    164.                 ENDCG
    165.             }
    166.         }
    167.     }
    168. }
    169.  
     
  26. Ben-BearFish

    Ben-BearFish

    Joined:
    Sep 6, 2011
    Posts:
    1,204
    Is there a way to get the blur shaders with grab passes optimized on mobile, or is that pretty much out of the question?
     
  27. alkaitagi

    alkaitagi

    Joined:
    Dec 8, 2016
    Posts:
    87
    Hi, I have a problem with the shader. In my game I use dark themed UI, i.e. every image has black color(with different transparency). When I put "bright" colors (like red, white, yellow etc.) all is ok, but the shader just does not work with black color. It produces blur effect, but there is no tint color. What can you suggest?
     
  28. hippocoder

    hippocoder

    Digital Ape

    Joined:
    Apr 11, 2010
    Posts:
    29,723
    You should do your entire ui in white, and tint it back down to dark on Unity's side. Because you can't make something brighter by tinting it but you can make it darker.
     
  29. alkaitagi

    alkaitagi

    Joined:
    Dec 8, 2016
    Posts:
    87
    When I adjust TintColor in material's inspector nothing happens. The blur affected only by image's color.
     
  30. alkaitagi

    alkaitagi

    Joined:
    Dec 8, 2016
    Posts:
    87
    I have fixed that, but again, when TintColor set to black and image color to white, it has no effect, blur still purely white.
     
  31. Seyed_Morteza_Kamaly

    Seyed_Morteza_Kamaly

    Joined:
    Nov 18, 2015
    Posts:
    80
  32. sajjadminaie

    sajjadminaie

    Joined:
    Jul 6, 2017
    Posts:
    2
  33. OmarVector

    OmarVector

    Joined:
    Apr 18, 2018
    Posts:
    129
    it render black on mobile sadly
     
  34. musheerxcubelabs

    musheerxcubelabs

    Joined:
    Nov 5, 2019
    Posts:
    2
    This shader is not working when CANVAS render mode set to be World Space and CAMERA projection set to be Perspective. Please help
     
  35. hojjat

    hojjat

    Joined:
    Jul 24, 2018
    Posts:
    2
    This shader can not be used as multiple items at the same time. For example, I used this shader for scrolling items and only one of the items worked properly.
    Is there another shade with the right quality that can be used in multiples?
     
  36. Seyed_Morteza_Kamaly

    Seyed_Morteza_Kamaly

    Joined:
    Nov 18, 2015
    Posts:
    80
    can you send a screenshot that shows the problem?
    did you use my shader?
     
  37. Seyed_Morteza_Kamaly

    Seyed_Morteza_Kamaly

    Joined:
    Nov 18, 2015
    Posts:
    80
  38. michealcaj

    michealcaj

    Joined:
    Aug 18, 2017
    Posts:
    191
    Coool
     
  39. DryreL

    DryreL

    Joined:
    Feb 23, 2020
    Posts:
    49
    Hi, I'm looking for smooth blur. Can someone share with us?
     
    dkdhunnoo likes this.
  40. nilpunch

    nilpunch

    Joined:
    Feb 24, 2022
    Posts:
    1
    Hi, if anyone care, i'm refactor and basically make my own version of UI blur shader with advantages.

    May be used to make blury BG in pause menu with render texture.

    It is fully compatible with UI, has alpha clipping and more. In comparison with other variants, it's highly maintainable and has all hardcode gone. It is now truely weight-based, and you can easily create your own dispersion shape.

    No GrabPasses used, since they are expensive in terms of performance (https://docs.unity3d.com/Manual/SL-GrabPass.html), they are not compatible with URP, HDRP and SRP, not compatible with mobile and just hard to work with.

    Gist: https://gist.github.com/nilpunch/2060a180e36bf610171d0525375ba13f

    Here is it:

    Code (CSharp):
    1. Shader "UI/BlurImage"
    2. {
    3.     Properties
    4.     {
    5.         [PerRendererData] _MainTex ("Sprite Texture", 2D) = "white" {}
    6.         _Color ("Tint", Color) = (1,1,1,1)
    7.        
    8.         [Space(50)]
    9.         _BlurX ("X Blur", Range(0.0, 0.5)) = 0.001
    10.         _BlurY ("Y Blur", Range(0.0, 0.5)) = 0.001
    11.        
    12.         [Space]
    13.         _Focus ("Focus", Range(0.0, 1.0)) = 0
    14.         _Distribution ("Distribution", Range(0.0, 1.0)) = 0.18
    15.         _Iterations ("Iterations", Integer) = 5
    16.        
    17.         [Space(50)]
    18.         _StencilComp ("Stencil Comparison", Float) = 8
    19.         _Stencil ("Stencil ID", Float) = 0
    20.         _StencilOp ("Stencil Operation", Float) = 0
    21.         _StencilWriteMask ("Stencil Write Mask", Float) = 255
    22.         _StencilReadMask ("Stencil Read Mask", Float) = 255
    23.  
    24.         _ColorMask ("Color Mask", Float) = 15
    25.  
    26.         [Toggle(UNITY_UI_ALPHACLIP)] _UseUIAlphaClip ("Use Alpha Clip", Float) = 0
    27.      }
    28.  
    29.     SubShader
    30.     {
    31.         Tags
    32.         {
    33.             "Queue"="Transparent"
    34.             "IgnoreProjector"="True"
    35.             "RenderType"="Transparent"
    36.             "PreviewType"="Plane"
    37.             "CanUseSpriteAtlas"="True"
    38.         }
    39.  
    40.         Stencil
    41.         {
    42.             Ref [_Stencil]
    43.             Comp [_StencilComp]
    44.             Pass [_StencilOp]
    45.             ReadMask [_StencilReadMask]
    46.             WriteMask [_StencilWriteMask]
    47.         }
    48.  
    49.         Cull Off
    50.         Lighting Off
    51.         ZWrite Off
    52.         ZTest [unity_GUIZTestMode]
    53.         Blend SrcAlpha OneMinusSrcAlpha
    54.         ColorMask [_ColorMask]
    55.  
    56.         Pass
    57.         {
    58.             Name "Default"
    59.         CGPROGRAM
    60.             #pragma vertex vert
    61.             #pragma fragment frag
    62.             #pragma target 2.0
    63.  
    64.             #include "UnityCG.cginc"
    65.             #include "UnityUI.cginc"
    66.  
    67.             #pragma multi_compile_local _ UNITY_UI_CLIP_RECT
    68.             #pragma multi_compile_local _ UNITY_UI_ALPHACLIP
    69.  
    70.             struct appdata_t
    71.             {
    72.                 float4 vertex   : POSITION;
    73.                 float4 color    : COLOR;
    74.                 float2 texcoord : TEXCOORD0;
    75.                 UNITY_VERTEX_INPUT_INSTANCE_ID
    76.             };
    77.  
    78.             struct v2f
    79.             {
    80.                 float4 vertex   : SV_POSITION;
    81.                 fixed4 color    : COLOR;
    82.                 float2 texcoord  : TEXCOORD0;
    83.                 float2 worldPosition  : TEXCOORD1;
    84.                 UNITY_VERTEX_OUTPUT_STEREO
    85.             };
    86.  
    87.             sampler2D _MainTex;
    88.             fixed4 _Color;
    89.        
    90.             fixed _BlurX;
    91.             fixed _BlurY;
    92.        
    93.             fixed _Focus;
    94.             fixed _Distribution;
    95.             int _Iterations;
    96.        
    97.             fixed4 _TextureSampleAdd;
    98.             float4 _ClipRect;
    99.             float4 _MainTex_ST;
    100.  
    101.             float4 tex2Dblur(float2 position, float2 offset)
    102.             {
    103.                 const float2 blur_offset = position.xy + float2(_BlurX, _BlurY).xy * offset * (1 - _Focus);
    104.                 return tex2D(_MainTex, blur_offset) + _TextureSampleAdd;
    105.             }
    106.  
    107.             float calculateWeight(float distance)
    108.             {
    109.                 return lerp(1, _Distribution, distance);
    110.             }
    111.  
    112.             v2f vert(appdata_t v)
    113.             {
    114.                 v2f OUT;
    115.                 UNITY_SETUP_INSTANCE_ID(v);
    116.                 UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(OUT);
    117.  
    118.                 OUT.worldPosition = v.vertex;
    119.                 OUT.vertex = UnityObjectToClipPos(v.vertex);
    120.                 OUT.texcoord = TRANSFORM_TEX(v.texcoord, _MainTex);
    121.                 OUT.color = v.color * _Color;
    122.                 return OUT;
    123.             }
    124.  
    125.             fixed4 frag(v2f IN) : SV_Target
    126.             {
    127.                 const int2 iterations = int2(_Iterations, _Iterations);
    128.                 const float centralPixelWeight = 1;
    129.  
    130.                 float4 color_sum = float4(0,0,0,0);
    131.                 float weight_sum = 0;
    132.  
    133.                 // Add central pixel
    134.                 color_sum += tex2Dblur(IN.texcoord, float2(0, 0)) * centralPixelWeight;
    135.                 weight_sum += centralPixelWeight;
    136.  
    137.                 // Add central column
    138.                 for (int horizontal = 1; horizontal < iterations.x; ++horizontal)
    139.                 {
    140.                     const float offset = (float)horizontal / iterations.x;
    141.                     const float weight = calculateWeight(offset);
    142.                    
    143.                     color_sum += tex2Dblur(IN.texcoord, float2(offset, 0)) * weight;
    144.                     color_sum += tex2Dblur(IN.texcoord, float2(-offset, 0)) * weight;
    145.                     weight_sum += weight * 2;
    146.                 }
    147.  
    148.                 // Add central row
    149.                 for (int vertical = 1; vertical < iterations.y; ++vertical)
    150.                 {
    151.                     const float offset = (float)vertical / iterations.y;
    152.                     const float weight = calculateWeight(offset);
    153.                    
    154.                     color_sum += tex2Dblur(IN.texcoord, float2(0, offset)) * weight;
    155.                     color_sum += tex2Dblur(IN.texcoord, float2(0, -offset)) * weight;
    156.                     weight_sum += weight * 2;
    157.                 }
    158.  
    159.                 // Add quads
    160.                 for (int x = 1; x < iterations.x; ++x)
    161.                 {
    162.                     for (int y = 1; y < iterations.y; ++y)
    163.                     {
    164.                         float2 offset = float2((float)x / iterations.x, (float)y / iterations.y);
    165.                         const float offsetLength = length(offset);
    166.                         const float weight = calculateWeight(offsetLength);
    167.                        
    168.                         color_sum += tex2Dblur(IN.texcoord, float2(offset.x, offset.y)) * weight;
    169.                         color_sum += tex2Dblur(IN.texcoord, float2(-offset.x, offset.y)) * weight;
    170.                         color_sum += tex2Dblur(IN.texcoord, float2(-offset.x, -offset.y)) * weight;
    171.                         color_sum += tex2Dblur(IN.texcoord, float2(offset.x, -offset.y)) * weight;
    172.                         weight_sum += weight * 4;
    173.                     }
    174.                 }
    175.  
    176.                 float4 final_color = color_sum / weight_sum * IN.color;
    177.  
    178.                 #ifdef UNITY_UI_CLIP_RECT
    179.                 final_color.a *= UnityGet2DClipping(IN.worldPosition.xy, _ClipRect);
    180.                 #endif
    181.  
    182.                 #ifdef UNITY_UI_ALPHACLIP
    183.                 clip(final_color.a - 0.001);
    184.                 #endif
    185.                
    186.                 return final_color;
    187.             }
    188.         ENDCG
    189.         }
    190.     }
    191. }
     
    Last edited: Jun 1, 2022
    Leonin, bb8_1, ezhaac and 2 others like this.
  41. ShervinM

    ShervinM

    Joined:
    Sep 16, 2017
    Posts:
    67
    I'm curious how to use this shader. I've created an image in my canvas, and added a material with this shader, but all I see is a blank white box.
     
    MrDizzle26 likes this.
  42. MrDizzle26

    MrDizzle26

    Joined:
    Feb 8, 2015
    Posts:
    36

    Yeah, I'm using this with a Blit and it comes back transparent for me
     
  43. FrankySteel

    FrankySteel

    Joined:
    Mar 24, 2020
    Posts:
    1
    @nilpunch Doesn't work for me either, have you tested this?
     
  44. Leonin

    Leonin

    Joined:
    Nov 13, 2014
    Posts:
    4
    After some experiments, i make it works.

    In my case i needed blur main canvas and show popups or loading spiner under it. I created 2 canvases and 2 cameras. First camera render main canvas that will blured, second camera render raw blured image and unblured content after that.

    So my hierarchy looks like this. Red arrows shows what canvas is rendere by camera.
    upload_2023-10-30_1-49-42.png

    Camera setups:
    upload_2023-10-30_1-53-14.png

    upload_2023-10-30_1-54-20.png

    Under [UI_AFTER_BLUR] canvas i created game object with Raw Image, its render RenderTexture. Note that you dont need set material on this Raw Image component.
    upload_2023-10-30_1-51-1.png

    On first main camera i attached script BlurBlitter, that shot camera screen and copy it to render texture named MainUIRender. Doesnt matter, is it asset or you creating RenderTexture at runtime.

    Code:

    Code (CSharp):
    1. using UnityEngine;
    2.  
    3. namespace Leonin
    4. {
    5.     [RequireComponent(typeof(Camera))]
    6.     public class BlurBlitter : MonoBehaviour
    7.     {
    8.         //TODO Replace to config
    9.         [SerializeField] private float blitDelay;
    10.  
    11.         [SerializeField] private RenderTexture targetRenderTexture;
    12.         [SerializeField] private Material blurMaterial;
    13.  
    14.         private RenderTexture temporaryRenderTexture;
    15.         private Camera mainCamera;
    16.         private float timer;
    17.         private bool blitOn;
    18.  
    19.         public bool BlitOn
    20.         {
    21.             get => blitOn;
    22.             set
    23.             {
    24.                 blitOn = value;
    25.  
    26.                 mainCamera.enabled = !blitOn;
    27.                 timer = blitOn ? blitDelay : 0;
    28.             }
    29.         }
    30.  
    31.         private void Awake()
    32.         {
    33.             mainCamera = GetComponent<Camera>();
    34.             targetRenderTexture.width = Screen.width;
    35.             targetRenderTexture.height = Screen.height;
    36.         }
    37.  
    38.         private void Update()
    39.         {
    40.             if (!BlitOn)
    41.             {
    42.                 return;
    43.             }
    44.  
    45.             timer += Time.deltaTime;
    46.         }
    47.  
    48.         private void LateUpdate()
    49.         {
    50.             if (timer > blitDelay)
    51.             {
    52.                 timer -= blitDelay;
    53.                 mainCamera.Render();
    54.             }
    55.         }
    56.  
    57.         private void OnPostRender()
    58.         {
    59.             if (!BlitOn)
    60.             {
    61.                 return;
    62.             }
    63.  
    64.             Graphics.Blit(mainCamera.activeTexture, targetRenderTexture, blurMaterial);
    65.             mainCamera.targetTexture = null;
    66.             RenderTexture.ReleaseTemporary(temporaryRenderTexture);
    67.         }
    68.  
    69.         private void OnPreRender()
    70.         {
    71.             if (!BlitOn)
    72.             {
    73.                 return;
    74.             }
    75.  
    76.             temporaryRenderTexture = RenderTexture.GetTemporary(Screen.width, Screen.height);
    77.             mainCamera.targetTexture = temporaryRenderTexture;
    78.         }
    79.  
    80.         private void OnValidate()
    81.         {
    82.             if (blitDelay < 0)
    83.             {
    84.                 blitDelay = 0;
    85.             }
    86.         }
    87.     }
    88. }
    89.  
    Render() calls render callbacks where happens all magic.

    In another client script you should change BlitOn. Also i added blit delay for FPS to configure how often image blur processing will occur.

    In my example, i use DoTween for smooth change focus and control second camera:

    Code (CSharp):
    1. using DG.Tweening;
    2. using DG.Tweening.Core;
    3. using System.Threading.Tasks;
    4. using UnityEngine;
    5.  
    6. namespace Leonin
    7. {
    8.     public class BlurService : MonoBehaviour
    9.     {
    10.         [SerializeField] private BlurBlitter blitter;
    11.         [SerializeField] private Material blurMaterial;
    12.         [SerializeField] private Camera afterBlurCamera;
    13.  
    14.         private const string SHADER_BLUR_VALUE_NAME = "_Focus";
    15.  
    16.         private const float MIN_BLUR = 0.95F;
    17.         private const float MAX_BLUR = 1F;
    18.  
    19.         private void Awake()
    20.         {
    21.             blurMaterial.SetFloat(SHADER_BLUR_VALUE_NAME, MAX_BLUR);
    22.         }
    23.  
    24.         public async Task SetBlur(float value, float duration)
    25.         {
    26.             if (value > 0)
    27.             {
    28.                 blitter.BlitOn = true;
    29.                 afterBlurCamera.enabled = true;
    30.             }
    31.  
    32.             if (duration == 0)
    33.             {
    34.                 blurMaterial.SetFloat(SHADER_BLUR_VALUE_NAME, Normalize(value));
    35.                 return;
    36.             }
    37.  
    38.             DOGetter<float> blurGetter = () => blurMaterial.GetFloat(SHADER_BLUR_VALUE_NAME);
    39.             DOSetter<float> blurSetter = (v) => blurMaterial.SetFloat(SHADER_BLUR_VALUE_NAME, v);
    40.  
    41.             await DOTween.To(blurGetter, blurSetter, Normalize(value), duration).AsyncWaitForCompletion();
    42.  
    43.             if (value == 0)
    44.             {
    45.                 blitter.BlitOn = false;
    46.                 afterBlurCamera.enabled = false;
    47.             }
    48.         }
    49.  
    50.         private static float Normalize(float value)
    51.         {
    52.             float invert = 1 - value;
    53.             float normalized = MIN_BLUR + invert * (MAX_BLUR - MIN_BLUR);
    54.             return normalized;
    55.         }
    56.     }
    57. }
    58.  
     
    Last edited: Oct 29, 2023