Search Unity

Question UI Canvas Postprocessing Shadows, How to?

Discussion in 'Shaders' started by Mhmd-Subhi, May 23, 2020.

  1. Mhmd-Subhi

    Mhmd-Subhi

    Joined:
    Mar 13, 2015
    Posts:
    28
    Hello everyone,

    I have a UI shader that draws a drop shadow for UI elements. First it draws the shadow on the first pass, with an offset, then it draws the actual sprite on the second pass. The problem is, when UI objects intersects each other, the shadows at the intersection adds up, as shown in the next figure.

    UI Shadow Shader 1.png

    The UI Shadow Shader:

    Code (CSharp):
    1. Shader "UnityCommunity/Sprites/SpriteDropShadow"
    2. {
    3.     Properties
    4.     {
    5.         [PerRendererData] _MainTex ("Sprite Texture", 2D) = "white" {}
    6.         _Color ("Tint", Color) = (1,1,1,1)
    7.         [MaterialToggle] PixelSnap ("Pixel snap", Float) = 0
    8.         _ShadowColor ("Shadow", Color) = (0,0,0,1)
    9.         _ShadowOffset ("ShadowOffset", Vector) = (0,-0.1,0,0)
    10.     }
    11.  
    12.     SubShader
    13.     {
    14.         Tags
    15.         {
    16.             "Queue"="Transparent"
    17.             "IgnoreProjector"="True"
    18.             "RenderType"="Transparent"
    19.             "PreviewType"="Plane"
    20.             "CanUseSpriteAtlas"="True"
    21.         }
    22.  
    23.         Cull Off
    24.         Lighting Off
    25.         ZWrite Off
    26.         Blend One OneMinusSrcAlpha
    27.  
    28.         // draw shadow
    29.         Pass
    30.         {
    31.         CGPROGRAM
    32.             #pragma vertex vert
    33.             #pragma fragment frag
    34.             #pragma multi_compile _ PIXELSNAP_ON
    35.             #include "UnityCG.cginc"
    36.            
    37.             struct appdata_t
    38.             {
    39.                 float4 vertex   : POSITION;
    40.                 float4 color    : COLOR;
    41.                 float2 texcoord : TEXCOORD0;
    42.             };
    43.  
    44.             struct v2f
    45.             {
    46.                 float4 vertex   : SV_POSITION;
    47.                 fixed4 color    : COLOR;
    48.                 float2 texcoord  : TEXCOORD0;
    49.             };
    50.            
    51.             fixed4 _Color;
    52.             fixed4 _ShadowColor;
    53.             float4 _ShadowOffset;
    54.  
    55.             v2f vert(appdata_t IN)
    56.             {
    57.                 v2f OUT;
    58.                 OUT.vertex = UnityObjectToClipPos(IN.vertex+_ShadowOffset);
    59.                 OUT.texcoord = IN.texcoord;
    60.                 OUT.color = IN.color *_ShadowColor;
    61.                 #ifdef PIXELSNAP_ON
    62.                 OUT.vertex = UnityPixelSnap (OUT.vertex);
    63.                 #endif
    64.  
    65.                 return OUT;
    66.             }
    67.  
    68.             sampler2D _MainTex;
    69.             sampler2D _AlphaTex;
    70.             float _AlphaSplitEnabled;
    71.  
    72.             fixed4 SampleSpriteTexture (float2 uv)
    73.             {
    74.                 fixed4 color = tex2D (_MainTex, uv).a;
    75.                 color.rgb = _ShadowColor.rgb;
    76.  
    77.                 #if UNITY_TEXTURE_ALPHASPLIT_ALLOWED
    78.                 if (_AlphaSplitEnabled)
    79.                     color.a = tex2D (_AlphaTex, uv).r;
    80.                 #endif //UNITY_TEXTURE_ALPHASPLIT_ALLOWED
    81.  
    82.                 return color;
    83.             }
    84.  
    85.             fixed4 frag(v2f IN) : SV_Target
    86.             {
    87.                 fixed4 c = SampleSpriteTexture (IN.texcoord) * IN.color;
    88.                 c.rgb *= c.a;
    89.                 return c;
    90.             }
    91.         ENDCG
    92.         }
    93.  
    94.         // draw real sprite
    95.         Pass
    96.         {
    97.         CGPROGRAM
    98.             #pragma vertex vert
    99.             #pragma fragment frag
    100.             #pragma multi_compile _ PIXELSNAP_ON
    101.             #include "UnityCG.cginc"
    102.            
    103.             struct appdata_t
    104.             {
    105.                 float4 vertex   : POSITION;
    106.                 float4 color    : COLOR;
    107.                 float2 texcoord : TEXCOORD0;
    108.             };
    109.  
    110.             struct v2f
    111.             {
    112.                 float4 vertex   : SV_POSITION;
    113.                 fixed4 color    : COLOR;
    114.                 float2 texcoord  : TEXCOORD0;
    115.             };
    116.            
    117.             fixed4 _Color;
    118.  
    119.             v2f vert(appdata_t IN)
    120.             {
    121.                 v2f OUT;
    122.                 OUT.vertex = UnityObjectToClipPos(IN.vertex);
    123.                 OUT.texcoord = IN.texcoord;
    124.                 OUT.color = IN.color * _Color;
    125.                 #ifdef PIXELSNAP_ON
    126.                 OUT.vertex = UnityPixelSnap (OUT.vertex);
    127.                 #endif
    128.  
    129.                 return OUT;
    130.             }
    131.  
    132.             sampler2D _MainTex;
    133.             sampler2D _AlphaTex;
    134.             float _AlphaSplitEnabled;
    135.  
    136.             fixed4 SampleSpriteTexture (float2 uv)
    137.             {
    138.                 fixed4 color = tex2D (_MainTex, uv);
    139.  
    140.                 #if UNITY_TEXTURE_ALPHASPLIT_ALLOWED
    141.                 if (_AlphaSplitEnabled)
    142.                     color.a = tex2D (_AlphaTex, uv).r;
    143.                 #endif //UNITY_TEXTURE_ALPHASPLIT_ALLOWED
    144.  
    145.                 return color;
    146.             }
    147.  
    148.             fixed4 frag(v2f IN) : SV_Target
    149.             {
    150.                 fixed4 c = SampleSpriteTexture (IN.texcoord) * IN.color;
    151.                 c.rgb *= c.a;
    152.                 return c;
    153.             }
    154.         ENDCG
    155.         }
    156.     }
    157. }
    I figured that this probably can be done if the shadow pass was done for the canvas as a whole, perhaps as a postprocessing, but anyway, where can I start? Or if there is another way this can be done.

    Thanks everyone,
    and stay safe.
     
  2. FlyingOreos

    FlyingOreos

    Joined:
    Mar 24, 2014
    Posts:
    12
    You could setup a replacement shader to render before the UI camera.

    https://docs.unity3d.com/Manual/SL-ShaderReplacement.html

    You would have to give your current shader a unique RenderType tag and render all of those objects into a RenderTexutre.
    Then as you're kindof suggesting, using a shader to render the full shadow texture onto the screen, all at once. The shader itself should be pretty simple to write.
    The only problem you're going to face with this method, is that as soon as you need to layer your shadows on top of other UI, you need to introduce that to your rendered shadow-mask, and full screen shadow shader somehow.
     
  3. FlyingOreos

    FlyingOreos

    Joined:
    Mar 24, 2014
    Posts:
    12
    Alternatively, have a look if there's any blendmode other than premultiply you can use for the shadow
    https://docs.unity3d.com/Manual/SL-Blend.html

    Maybe something like BlendOp Min? I would imagine that could mean rendering the lowest value of foreground/background.
    I haven't tried that though
     
    Last edited: May 23, 2020
  4. Mhmd-Subhi

    Mhmd-Subhi

    Joined:
    Mar 13, 2015
    Posts:
    28
    @FlyingOreos
    Thank you, those were a very good starting points.
    I think I will use the render texture technique, because I also want to add anti-aliasing to the texture, although layering could prove difficult with other objects. The good thing is that these objects I want the shadow for are for the gameplay, so there is only the background behind them, and every thing else is above them.

    Another problem that I would like to solve, is that we want to make like in this image:
    UI Shadow Shader 2.png

    Easier said than done, we want to make an illusion using an extra shadow at the intersection to indicate depth. I think this would require to write our own depth detection method.
     
    Last edited: May 24, 2020