Search Unity

  1. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Resolved Trying to create an additive glow that only illuminates certain layers

Discussion in '2D' started by GrrdMlmr, Jun 29, 2023.

  1. GrrdMlmr

    GrrdMlmr

    Joined:
    Aug 12, 2017
    Posts:
    5
    Hello! In my 2D platformer I am using a sprite with a custom additive shader to illuminate objects in a simple retro fashion. Currently it has the effect of illuminating the "near" background as well as the "far" background, as demonstrated in this screenshot:

    Shader_Help_1.png

    I am seeking to accomplish a similar effect but without illuminating the "far" background, as illustrated in this mockup:

    Shader_Help_2.png

    Here is the code for the shader:

    Code (CSharp):
    1. Shader "Custom/Additive"
    2. {
    3.     Properties
    4.     {
    5.         [PerRendererData] _MainTex ("Sprite Texture", 2D) = "white" {}
    6.         [HDR]_Color ("Tint", Color) = (1,1,1,1)
    7.         [MaterialToggle] PixelSnap ("Pixel snap", Float) = 0
    8.         [HideInInspector] _RendererColor ("RendererColor", Color) = (1,1,1,1)
    9.         [HideInInspector] _Flip ("Flip", Vector) = (1,1,1,1)
    10.         [PerRendererData] _AlphaTex ("External Alpha", 2D) = "white" {}
    11.         [PerRendererData] _EnableExternalAlpha ("Enable External Alpha", Float) = 0
    12.     }
    13.  
    14.     SubShader
    15.     {
    16.         Tags
    17.         {
    18.             "Queue"="Transparent"
    19.             "IgnoreProjector"="True"
    20.             "RenderType"="Transparent"
    21.             "PreviewType"="Plane"
    22.             "CanUseSpriteAtlas"="True"
    23.         }
    24.  
    25.         Cull Off
    26.         Lighting Off
    27.         ZWrite Off
    28.         Blend DstColor DstAlpha
    29.  
    30.         Pass
    31.         {
    32.             CGPROGRAM
    33.                 #pragma vertex SpriteVert
    34.                 #pragma fragment SpriteFrag
    35.                 #pragma target 2.0
    36.                 #pragma multi_compile_instancing
    37.                 #pragma multi_compile_local _ PIXELSNAP_ON
    38.                 #pragma multi_compile _ ETC1_EXTERNAL_ALPHA
    39.                 #include "UnitySprites.cginc"
    40.             ENDCG
    41.         }
    42.     }
    43. }
    So far I've explored multiple-camera solutions to no avail, and my understanding of shaders is unfortunately very rudimentary. I expect the simplest solution would be to change the sky background shader. The tilemaps and objects in the scene use a modified sprite shader that allows basic pallette swapping, and the sky background uses the default sprite shader. All renderers are on the default Sorting Layer, and arranged using the Order in Layer value. A highly performant solution is especially preferable as I am targeting lower-end hardware.

    Thanks!
     
    Last edited: Jun 29, 2023
  2. venediklee

    venediklee

    Joined:
    Jul 24, 2017
    Posts:
    202
    I don't know about your shader, but even with official 2d point light, if you move the objects away from the light(Z direction) they should stop receiving the lights after a point because that object ends up not being close enough to the light source. So, just move the background away?
     
  3. GrrdMlmr

    GrrdMlmr

    Joined:
    Aug 12, 2017
    Posts:
    5
    Apologies, I should have specified that I'm using the Built-In Render Pipeline and so I don't have access to Unity's 2D lights. My shader is attached to a Sprite Renderer so no true lighting is taking place. I won't completely rule out switching between pipelines, but I expect it may be more trouble than it's worth for my year-old project.
     
    Last edited: Jun 30, 2023
  4. venediklee

    venediklee

    Joined:
    Jul 24, 2017
    Posts:
    202
    I know, your shader is cgprogram which means your project is built-in pipeline

    What I meant was, try moving the background sprite's Z position to see how it effects the lighting. In most cases the lights(even 2d lights) calculate the distance using the XYZ coordinates instead of using just the XY coordinates
     
  5. venediklee

    venediklee

    Joined:
    Jul 24, 2017
    Posts:
    202
    LOL, I didn't see this so ignore my above comment.

    Why can't you use another material for the background?
     
  6. GrrdMlmr

    GrrdMlmr

    Joined:
    Aug 12, 2017
    Posts:
    5
    There isn't anything fancy going on with the background so I can certainly use another material, I just haven't figured out what would work. I'm not bound to the default sprite shader I'm currently using.
     
  7. venediklee

    venediklee

    Joined:
    Jul 24, 2017
    Posts:
    202
    This is just a simple shader update then.

    Find the SpriteFrag method(where ever it is); it’ll have some way of multiplying the color of the fragment(pixel) color with light color. You can then just create another shader without the fragment light calculations and use it for background

    Possibly the light calculations could be on the vertex shader instead, you’ll just have to find where exactly light calculations are done

    we might be able to help more if you can post the actual shaders(dont post it if it isnt open source etc.)

    PS: for most performance, remove useless calculations from vertex and fragment shaders. PS2:compiler might remove them when compiling even if you dont
     
  8. GrrdMlmr

    GrrdMlmr

    Joined:
    Aug 12, 2017
    Posts:
    5
    Alright, I've made some progress here with a different approach by drawing my sprites to a stencil buffer and only drawing the light over the stencil. The only issue now is that the PixelColors shader I use for pallette swapping doesn't clip transparent pixels, so the invisible areas of my sprites are illuminated like this:

    Shader_Help_4.png

    I attempted to add alpha clipping to the shader but everything I tried broke it completely. I'm sure it wouldn't be overly complex to accomplish but I have run up against the limits of my understanding of shaders.

    Here's the code for the shader I'm using on pretty much everything I want to be illuminated:

    Code (CSharp):
    1. Shader "Custom/PixelColors" {
    2.     Properties
    3.     {
    4.         [PerRendererData] _MainTex ("Sprite Texture", 2D) = "white" {}
    5.         _ColorTint ("Tint", Color) = (1,1,1,1)
    6.         _Color1in ("Color 1 In", Color) = (1,1,1,1)
    7.         _Color1out ("Color 1 Out", Color) = (1,1,1,1)
    8.         _Color2in ("Color 2 In", Color) = (1,1,1,1)
    9.         _Color2out ("Color 2 Out", Color) = (1,1,1,1)
    10.         _Color3in ("Color 3 In", Color) = (1,1,1,1)
    11.         _Color3out ("Color 3 Out", Color) = (1,1,1,1)
    12.         _Color4in ("Color 4 In", Color) = (1,1,1,1)
    13.         _Color4out ("Color 4 Out", Color) = (1,1,1,1)
    14.         [MaterialToggle] PixelSnap ("Pixel snap", Float) = 0
    15.     }
    16.     SubShader
    17.     {
    18.         Tags
    19.         {
    20.             "Queue"="Transparent"
    21.             "IgnoreProjector"="True"
    22.             "RenderType"="Transparent"
    23.             "PreviewType"="Plane"
    24.             "CanUseSpriteAtlas"="True"
    25.         }
    26.         Cull Off
    27.         Lighting Off
    28.         ZWrite Off
    29.         Fog { Mode Off }
    30.         Blend SrcAlpha OneMinusSrcAlpha
    31.  
    32.         Stencil {
    33.             Ref 1
    34.             Comp Always
    35.             Pass Replace
    36.             }
    37.         Pass
    38.         {
    39.         CGPROGRAM
    40.             #pragma vertex vert
    41.             #pragma fragment frag      
    42.             #pragma multi_compile DUMMY PIXELSNAP_ON
    43.             #include "UnityCG.cginc"
    44.        
    45.             struct appdata_t
    46.             {
    47.                 float4 vertex   : POSITION;
    48.                 float4 color    : COLOR;
    49.                 float2 texcoord : TEXCOORD0;
    50.             };
    51.             struct v2f
    52.             {
    53.                 float4 vertex   : SV_POSITION;
    54.                 fixed4 color    : COLOR;
    55.                 half2 texcoord  : TEXCOORD0;
    56.             };
    57.        
    58.             fixed4 _ColorTint;
    59.             fixed4 _Color1in;
    60.             fixed4 _Color1out;
    61.             fixed4 _Color2in;
    62.             fixed4 _Color2out;
    63.             fixed4 _Color3in;
    64.             fixed4 _Color3out;
    65.             fixed4 _Color4in;
    66.             fixed4 _Color4out;
    67.             v2f vert(appdata_t IN)
    68.             {
    69.                 v2f OUT;
    70.                 OUT.vertex = UnityObjectToClipPos(IN.vertex);
    71.                 OUT.texcoord = IN.texcoord;        
    72.                 OUT.color = IN.color * _ColorTint;
    73.                 #ifdef PIXELSNAP_ON
    74.                 OUT.vertex = UnityPixelSnap (OUT.vertex);
    75.                 #endif
    76.                 return OUT;
    77.             }
    78.             sampler2D _MainTex;    
    79.  
    80.             fixed4 frag(v2f IN) : COLOR
    81.             {
    82.                 float4 texColor = tex2D( _MainTex, IN.texcoord );
    83.                 texColor = all(texColor == _Color1in) ? _Color1out : texColor;
    84.                 texColor = all(texColor == _Color2in) ? _Color2out : texColor;
    85.                 texColor = all(texColor == _Color3in) ? _Color3out : texColor;
    86.                 texColor = all(texColor == _Color4in) ? _Color4out : texColor;
    87.              
    88.                 return texColor * IN.color;
    89.             }
    90.         ENDCG
    91.         }
    92.     }
    93. }
    Do you think you could modify this shader so that it discards transparent pixels before they are added to the stencil?
     
    Last edited: Jun 30, 2023
  9. venediklee

    venediklee

    Joined:
    Jul 24, 2017
    Posts:
    202
    Alpha clipping should work :thinking:

    in the fragment shader, after
    float4 texColor = tex2D( _MainTex, IN.texcoord );
    do something like

    Code (CSharp):
    1. clip(texColor .a - alphaCutoffValue);
    alphaCutoffValue can be a property or a constant; I don't remember if alpha range was between 0-1 or 0-255; try both!
     
    Chubzdoomer and GrrdMlmr like this.
  10. venediklee

    venediklee

    Joined:
    Jul 24, 2017
    Posts:
    202
    Oh btw they say clip is notorious for "bad performance". Setting the alpha of texColor to 0 instead of clipping was a recommendation, it obviously wont work if material is not transparent
     
  11. GrrdMlmr

    GrrdMlmr

    Joined:
    Aug 12, 2017
    Posts:
    5
    That worked perfectly. Performance doesn't seem to have taken a hit either. I'll figure out shaders one of these days. Thank you very much for all your help!
     
    venediklee likes this.