Search Unity

Modifying the Diffuse Sprite shader to cap brightness

Discussion in 'Shaders' started by FritzC, Dec 1, 2017.

  1. FritzC

    FritzC

    Joined:
    Nov 15, 2017
    Posts:
    15
    This is how the sprite looks with the default sprite shader:


    This is how it looks with the diffuse sprite shader and lights inserted:


    What I'm trying to do is cap the brightness of the lighting so that the brightest the sprite can be is the original sprite. Currently, as you can see, the lights distort the colors. I'd rather them just reveal the sprite they shine on in the darkness.

    I've followed a few tutorials and it seems that I will have to write a surface shader for this. I've taken the Unity Diffuse-Sprite shader and have been modifying it. To achieve my goal it seems to me that I have 2 options:
    • Compared pixels from the lit version of the sprite to pixels from the default version of the sprite and choose the darker one
    • Change how the lighting is blended into the sprites to some type of multiplicative lighting instead of the additive (i think?) current way of doing it.
    To do either of these I believe I will need to edit the fragment shader however I read that surface shaders generate their own frag functions so you cannot write one. Can I get some direction here? I'm utterly lost.

    My current shader:
    Code (csharp):
    1.  
    2. // Unity built-in shader source. Copyright (c) 2016 Unity Technologies. MIT license (see license.txt)
    3.  
    4. Shader "Custom/Capped_Diffuse"
    5. {
    6.     Properties
    7.     {
    8.         [PerRendererData] _MainTex ("Sprite Texture", 2D) = "white" {}
    9.         _Color ("Tint", Color) = (1,1,1,1)
    10.         [MaterialToggle] PixelSnap ("Pixel snap", Float) = 0
    11.         [HideInInspector] _RendererColor ("RendererColor", Color) = (1,1,1,1)
    12.         [HideInInspector] _Flip ("Flip", Vector) = (1,1,1,1)
    13.         [PerRendererData] _AlphaTex ("External Alpha", 2D) = "white" {}
    14.         [PerRendererData] _EnableExternalAlpha ("Enable External Alpha", Float) = 0
    15.     }
    16.  
    17.     SubShader
    18.     {
    19.         Tags
    20.         {
    21.             "Queue"="Transparent"
    22.             "IgnoreProjector"="True"
    23.             "RenderType"="Transparent"
    24.             "PreviewType"="Plane"
    25.             "CanUseSpriteAtlas"="True"
    26.         }
    27.  
    28.         Cull Off
    29.         Lighting Off
    30.         ZWrite Off
    31.         Blend One OneMinusSrcAlpha
    32.  
    33.         CGPROGRAM
    34.         #pragma surface surf SimpleLambert vertex:vert nofog nolightmap nodynlightmap keepalpha noinstancing
    35.         #pragma multi_compile _ PIXELSNAP_ON
    36.         #pragma multi_compile _ ETC1_EXTERNAL_ALPHA
    37.         #include "UnitySprites.cginc"
    38.    
    39.         half4 LightingSimpleLambert (SurfaceOutput s, half3 lightDir, half atten) {
    40.             half NdotL = dot (s.Normal, lightDir);
    41.             half4 c;
    42.             c.rgb = s.Albedo * _LightColor0.rgb * (NdotL * atten);
    43.             c.a = s.Alpha;
    44.             // It doesn't look like I have any sort of access to texture coordinates here so I
    45.             // don't think I can do it in this function
    46.             return c;
    47.         }
    48.  
    49.         struct Input
    50.         {
    51.             float2 uv_MainTex;
    52.             fixed4 color;
    53.         };
    54.  
    55.         void vert (inout appdata_full v, out Input o)
    56.         {
    57.             v.vertex.xy *= _Flip.xy;
    58.  
    59.             #if defined(PIXELSNAP_ON)
    60.             v.vertex = UnityPixelSnap (v.vertex);
    61.             #endif
    62.  
    63.             UNITY_INITIALIZE_OUTPUT(Input, o);
    64.             o.color = v.color * _Color * _RendererColor;
    65.         }
    66.  
    67.         void surf (Input IN, inout SurfaceOutput o)
    68.         {
    69.             fixed4 c = SampleSpriteTexture (IN.uv_MainTex) * IN.color;
    70.             o.Albedo = c.rgb * c.a;
    71.             o.Alpha = c.a;
    72.             // I'm not too sure what this does
    73.         }
    74.    
    75.         ENDCG
    76.     }
    77. }
    78.  
    And here is a version I attempted to make using a GrabPass to render the lit version and then in a fragment shader, select the darker of the two pixels from the lit and unlit sprites:
    Code (csharp):
    1.  
    2.  
    3. // Unity built-in shader source. Copyright (c) 2016 Unity Technologies. MIT license (see license.txt)
    4.  
    5. Shader "Custom/Capped_Diffuse"
    6. {
    7.     Properties
    8.     {
    9.         [PerRendererData] _MainTex ("Sprite Texture", 2D) = "white" {}
    10.         _Color ("Tint", Color) = (1,1,1,1)
    11.         [MaterialToggle] PixelSnap ("Pixel snap", Float) = 0
    12.         [HideInInspector] _RendererColor ("RendererColor", Color) = (1,1,1,1)
    13.         [HideInInspector] _Flip ("Flip", Vector) = (1,1,1,1)
    14.         [PerRendererData] _AlphaTex ("External Alpha", 2D) = "white" {}
    15.         [PerRendererData] _EnableExternalAlpha ("Enable External Alpha", Float) = 0
    16.     }
    17.  
    18.     SubShader
    19.     {
    20.         Tags
    21.         {
    22.             "Queue"="Transparent"
    23.             "IgnoreProjector"="True"
    24.             "RenderType"="Transparent"
    25.             "PreviewType"="Plane"
    26.             "CanUseSpriteAtlas"="True"
    27.         }
    28.  
    29.         Cull Off
    30.         Lighting Off
    31.         ZWrite Off
    32.         Blend One OneMinusSrcAlpha
    33.  
    34.         CGPROGRAM
    35.         #pragma surface surf Lambert vertex:vert nofog nolightmap nodynlightmap keepalpha noinstancing
    36.         #pragma multi_compile _ PIXELSNAP_ON
    37.         #pragma multi_compile _ ETC1_EXTERNAL_ALPHA
    38.         #include "UnitySprites.cginc"
    39.  
    40.         struct Input
    41.         {
    42.             float2 uv_MainTex;
    43.             fixed4 color;
    44.         };
    45.  
    46.         void vert (inout appdata_full v, out Input o)
    47.         {
    48.             v.vertex.xy *= _Flip.xy;
    49.  
    50.             #if defined(PIXELSNAP_ON)
    51.             v.vertex = UnityPixelSnap (v.vertex);
    52.             #endif
    53.  
    54.             UNITY_INITIALIZE_OUTPUT(Input, o);
    55.             o.color = v.color * _Color * _RendererColor;
    56.         }
    57.  
    58.         void surf (Input IN, inout SurfaceOutput o)
    59.         {
    60.             fixed4 c = SampleSpriteTexture (IN.uv_MainTex) * IN.color;
    61.             fixed4 c2 = tex2D(_MainTex, IN.uv_MainTex);
    62.             o.Albedo = c2.rgb * c.a;
    63.             o.Alpha = c.a;
    64.  
    65.         }
    66.    
    67.         ENDCG
    68.         GrabPass
    69.         {
    70.             "_LitTexture"
    71.         }
    72.         Pass
    73.         {
    74.         CGPROGRAM
    75.             #pragma vertex vert
    76.             #pragma fragment frag
    77.             #pragma multi_compile DUMMY PIXELSNAP_ON
    78.             #include "UnityCG.cginc"
    79.  
    80.             struct appdata_t
    81.             {
    82.                 float4 vertex   : POSITION;
    83.                 float4 color    : COLOR;
    84.                 float2 texcoord : TEXCOORD0;
    85.             };
    86.  
    87.             struct v2f
    88.             {
    89.                 float4 vertex   : SV_POSITION;
    90.                 fixed4 color    : COLOR;
    91.                 half2 texcoord  : TEXCOORD0;
    92.                 half4 grabPos  : TEXCOORD1;
    93.             };
    94.  
    95.             fixed4 _Color;
    96.  
    97.             v2f vert(appdata_t IN)
    98.             {
    99.                 v2f OUT;
    100.                 OUT.vertex = UnityObjectToClipPos(IN.vertex);
    101.                 OUT.texcoord = IN.texcoord;
    102.                 OUT.grabPos = ComputeGrabScreenPos(OUT.vertex);
    103.                 OUT.color = IN.color * _Color;
    104.                 #ifdef PIXELSNAP_ON
    105.                 OUT.vertex = UnityPixelSnap (OUT.vertex);
    106.                 #endif
    107.  
    108.                 return OUT;
    109.             }
    110.        
    111.             sampler2D _MainTex;
    112.             sampler2D _LitTexture;
    113.  
    114.             fixed4 frag(v2f IN) : SV_Target
    115.             {
    116.                 fixed4 c = tex2D(_MainTex, IN.texcoord) * IN.color;
    117.                 c.rgb *= c.a;
    118.                // return c;
    119.                 fixed4 color = tex2Dproj(_LitTexture, UNITY_PROJ_COORD(IN.grabPos));
    120.                 if (color.r > c.r || color.g > c.g || color.b > c.b) {
    121.                 return c;
    122.                 }
    123.                 return color;
    124.             }
    125.         ENDCG
    126.         }
    127.     }
    128. }
    129.  
    This one almost works however it makes some sprites disappear completely as if the rendering order is screwed up. Here is how it looks compared to the diffuse shader:
    vs
     
    Last edited: Dec 1, 2017
  2. brownboot67

    brownboot67

    Joined:
    Jan 5, 2013
    Posts:
    375
    You're going to need to write a custom lighting function and min the output color after its lit.
     
  3. FritzC

    FritzC

    Joined:
    Nov 15, 2017
    Posts:
    15
    That's what I assumed but I'm trying to understand exactly where I would do this in the shader.
     
  4. brownboot67

    brownboot67

    Joined:
    Jan 5, 2013
    Posts:
    375
  5. FritzC

    FritzC

    Joined:
    Nov 15, 2017
    Posts:
    15
  6. brownboot67

    brownboot67

    Joined:
    Jan 5, 2013
    Posts:
    375
    Code (csharp):
    1. c.rgb = min(s.Albedo * _LightColor0.rgb * (NdotL * atten), _MaxColor.rgb);
     
  7. FritzC

    FritzC

    Joined:
    Nov 15, 2017
    Posts:
    15
    I'm sorry, I didn't explain myself very well in the original post. I understand the logic of how I would do it, I just don't exactly understand how I would go about fetching the colors from the unlit versions of the sprites to compare to.

    To clarify a bit, I don't understand how I would fetch and set a variable akin to the _MaxColor one you use.
     
  8. brownboot67

    brownboot67

    Joined:
    Jan 5, 2013
    Posts:
    375
    Make a property at the top of your shader. Set the value in your material.