Search Unity

Trying to create a glowing halo around a 2D Sprite

Discussion in 'Shaders' started by Oonezumikamisama, Apr 7, 2017.

  1. Oonezumikamisama

    Oonezumikamisama

    Joined:
    Mar 7, 2017
    Posts:
    2
    I am trying to create a glowing halo around a 2D sprite. Mind you, I have no idea how to code a shader myself. I did find a Sprite Outline shader posted online by a fellow named Ryan Nielson, and in the comments somebody posted a modification to make the outline extend outward instead of inward. This seemed like a good start. The result reads like this:

    Code (CSharp):
    1. Shader "Sprites/Outline"
    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.  
    9.         // Add values to determine if outlining is enabled and outline color.
    10.         [PerRendererData] _Outline("Outline", Float) = 0
    11.         [PerRendererData] _OutlineColor("Outline Color", Color) = (1,1,1,1)
    12.         [PerRendererData] _OutlineSize("Outline Size", int) = 1
    13.     }
    14.  
    15.     SubShader
    16.     {
    17.         Tags
    18.         {
    19.             "Queue" = "Transparent"
    20.             "IgnoreProjector" = "True"
    21.             "RenderType" = "Transparent"
    22.             "PreviewType" = "Plane"
    23.             "CanUseSpriteAtlas" = "True"
    24.         }
    25.  
    26.         Cull Off
    27.         Lighting Off
    28.         ZWrite Off
    29.         Blend One OneMinusSrcAlpha
    30.  
    31.         Pass
    32.         {
    33.             CGPROGRAM
    34.             #pragma vertex vert
    35.             #pragma fragment frag
    36.             #pragma multi_compile _ PIXELSNAP_ON
    37.             #pragma shader_feature ETC1_EXTERNAL_ALPHA
    38.             #include "UnityCG.cginc"
    39.  
    40.             struct appdata_t
    41.             {
    42.                 float4 vertex   : POSITION;
    43.                 float4 color    : COLOR;
    44.                 float2 texcoord : TEXCOORD0;
    45.             };
    46.  
    47.             struct v2f
    48.             {
    49.                 float4 vertex   : SV_POSITION;
    50.                 fixed4 color : COLOR;
    51.                 float2 texcoord  : TEXCOORD0;
    52.             };
    53.  
    54.             fixed4 _Color;
    55.             float _Outline;
    56.             fixed4 _OutlineColor;
    57.             int _OutlineSize;
    58.  
    59.             v2f vert(appdata_t IN)
    60.             {
    61.                 v2f OUT;
    62.                 OUT.vertex = mul(UNITY_MATRIX_MVP, IN.vertex);
    63.                 OUT.texcoord = IN.texcoord;
    64.                 OUT.color = IN.color * _Color;
    65.                 #ifdef PIXELSNAP_ON
    66.                 OUT.vertex = UnityPixelSnap(OUT.vertex);
    67.                 #endif
    68.  
    69.                 return OUT;
    70.             }
    71.  
    72.             sampler2D _MainTex;
    73.             sampler2D _AlphaTex;
    74.             float4 _MainTex_TexelSize;
    75.  
    76.             fixed4 SampleSpriteTexture(float2 uv)
    77.             {
    78.                 fixed4 color = tex2D(_MainTex, uv);
    79.  
    80.                 #if ETC1_EXTERNAL_ALPHA
    81.                 // get the color from an external texture (usecase: Alpha support for ETC1 on android)
    82.                 color.a = tex2D(_AlphaTex, uv).r;
    83.                 #endif //ETC1_EXTERNAL_ALPHA
    84.  
    85.                 return color;
    86.             }
    87.  
    88.             fixed4 frag(v2f IN) : SV_Target
    89.             {
    90.                 fixed4 c = SampleSpriteTexture(IN.texcoord) * IN.color;
    91.  
    92.                 // If outline is enabled and there is a pixel, try to draw an outline.
    93.                     if (_Outline > 0 && c.a == 0) {
    94.                     float totalAlpha = 0.0;
    95.  
    96.                     [unroll(100)]
    97.                     for (int i = 1; i < _OutlineSize + 1; i++) {
    98.                         fixed4 pixelUp = tex2D(_MainTex, IN.texcoord + fixed2(0, i * _MainTex_TexelSize.y));
    99.                         fixed4 pixelDown = tex2D(_MainTex, IN.texcoord - fixed2(0,i *  _MainTex_TexelSize.y));
    100.                         fixed4 pixelRight = tex2D(_MainTex, IN.texcoord + fixed2(i * _MainTex_TexelSize.x, 0));
    101.                         fixed4 pixelLeft = tex2D(_MainTex, IN.texcoord - fixed2(i * _MainTex_TexelSize.x, 0));
    102.  
    103.                         totalAlpha = totalAlpha + pixelUp.a + pixelDown.a + pixelRight.a + pixelLeft.a;
    104.                     }
    105.  
    106.                     if (totalAlpha != 0) {
    107.                     c.rgba = fixed4(1, 1, 1, 1) * _OutlineColor;
    108.                     }
    109.                 }
    110.  
    111.                 c.rgb *= c.a;
    112.  
    113.                 return c;
    114.             }
    115.             ENDCG
    116.         }
    117.     }
    118. }
    There are three issues that I've encountered here, though. The first one, of course, is that this is an outline, not a glow. I need the outline's alpha to fade out the farther away from the sprite it gets; that ought to create a decent glowing effect, I think? But I don't really know how to do that.

    The second issue is that the aura doesn't extend in all directions, it only extends on the X and Y axes independently. That creates some concave corners in certain places, like so:


    The third issue is that the aura gets cropped. By changing a setting and adding a margin around my sprite, I managed to fix this in the editor, but at runtime, the sprite has some kind of bounding box that gets cropped in based on the sprite's image.


    Again, I don't really know how to code a shader, so I'm not really sure how to fix these issues. Any help would be greatly appreciated.
     
  2. Oonezumikamisama

    Oonezumikamisama

    Joined:
    Mar 7, 2017
    Posts:
    2
    With help from a friend, the shader has been modified as such:

    Code (CSharp):
    1. Shader "Sprites/Outline"
    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.  
    9.         // Add values to determine if outlining is enabled and outline color.
    10.         [PerRendererData] _Outline("Outline", Float) = 0
    11.         [PerRendererData] _OutlineColor("Outline Color", Color) = (1,1,1,1)
    12.         [PerRendererData] _OutlineSize("Outline Size", int) = 1
    13.     }
    14.  
    15.     SubShader
    16.     {
    17.         Tags
    18.         {
    19.             "Queue" = "Transparent"
    20.             "IgnoreProjector" = "True"
    21.             "RenderType" = "Transparent"
    22.             "PreviewType" = "Plane"
    23.             "CanUseSpriteAtlas" = "True"
    24.         }
    25.  
    26.         Cull Off
    27.         Lighting Off
    28.         ZWrite Off
    29.         Blend One OneMinusSrcAlpha
    30.  
    31.         Pass
    32.         {
    33.             CGPROGRAM
    34.             #pragma vertex vert
    35.             #pragma fragment frag
    36.             #pragma multi_compile _ PIXELSNAP_ON
    37.             #pragma shader_feature ETC1_EXTERNAL_ALPHA
    38.             #include "UnityCG.cginc"
    39.  
    40.             struct appdata_t
    41.             {
    42.                 float4 vertex   : POSITION;
    43.                 float4 color    : COLOR;
    44.                 float2 texcoord : TEXCOORD0;
    45.             };
    46.  
    47.             struct v2f
    48.             {
    49.                 float4 vertex   : SV_POSITION;
    50.                 fixed4 color : COLOR;
    51.                 float2 texcoord  : TEXCOORD0;
    52.             };
    53.  
    54.             fixed4 _Color;
    55.             float _Outline;
    56.             fixed4 _OutlineColor;
    57.             int _OutlineSize;
    58.  
    59.             v2f vert(appdata_t IN)
    60.             {
    61.                 v2f OUT;
    62.                 OUT.vertex = mul(UNITY_MATRIX_MVP, IN.vertex);
    63.                 OUT.texcoord = IN.texcoord;
    64.                 OUT.color = IN.color * _Color;
    65.                 #ifdef PIXELSNAP_ON
    66.                 OUT.vertex = UnityPixelSnap(OUT.vertex);
    67.                 #endif
    68.  
    69.                 return OUT;
    70.             }
    71.  
    72.             sampler2D _MainTex;
    73.             sampler2D _AlphaTex;
    74.             float4 _MainTex_TexelSize;
    75.  
    76.             fixed4 SampleSpriteTexture(float2 uv)
    77.             {
    78.                 fixed4 color = tex2D(_MainTex, uv);
    79.  
    80.                 #if ETC1_EXTERNAL_ALPHA
    81.                 // get the color from an external texture (usecase: Alpha support for ETC1 on android)
    82.                 color.a = tex2D(_AlphaTex, uv).r;
    83.                 #endif //ETC1_EXTERNAL_ALPHA
    84.  
    85.                 return color;
    86.             }
    87.  
    88.             fixed4 frag(v2f IN) : SV_Target
    89.             {
    90.                 fixed4 c = SampleSpriteTexture(IN.texcoord) * IN.color;
    91.  
    92.                 // If outline is enabled and there is a pixel, try to draw an outline.
    93.                     if (_Outline > 0 && c.a == 0) {
    94.                     float totalAlpha = 0.0;
    95.                     float distance = 0.0;
    96.  
    97.                     [unroll(100)]
    98.                     for (int i = 1; i < _OutlineSize + 1; i++) {
    99.                         fixed4 pixelUp = tex2D(_MainTex, IN.texcoord + fixed2(0, i * _MainTex_TexelSize.y));
    100.                         fixed4 pixelDown = tex2D(_MainTex, IN.texcoord - fixed2(0,i *  _MainTex_TexelSize.y));
    101.                         fixed4 pixelRight = tex2D(_MainTex, IN.texcoord + fixed2(i * _MainTex_TexelSize.x, 0));
    102.                         fixed4 pixelLeft = tex2D(_MainTex, IN.texcoord - fixed2(i * _MainTex_TexelSize.x, 0));
    103.                         if(pixelUp.a + pixelDown.a + pixelRight.a + pixelLeft.a > 0) { distance = i; break; }
    104.                     }
    105.  
    106.                     if (distance > 0) {
    107.                         float totalAlpha = 1 - ((distance * 1.0f)/_OutlineSize);
    108.                         c.rgba = fixed4(1, 1, 1, totalAlpha) * _OutlineColor;
    109.                     }
    110.                 }
    111.  
    112.                 c.rgb *= c.a;
    113.  
    114.                 return c;
    115.             }
    116.             ENDCG
    117.         }
    118.     }
    119. }
    Now the alpha gradates as the distance from the sprite increases, more like a proper glow. I'm still struggling with the other issues, though. I tried to fix the corners by adding checks along the diagonals, but that gave the corners a strange feathered look.