Search Unity

How do you use a Normal Map to apply a second texture without affecting light reflections?

Discussion in 'Shaders' started by SomeHumbleOnion, Nov 2, 2021.

  1. SomeHumbleOnion

    SomeHumbleOnion

    Joined:
    Jan 18, 2021
    Posts:
    28
    Hi everyone! I'm working on a toon lit shader that you can apply a second texture to (such as moss or snow). I'm going with the typical approach to this by using a normal map but the problem is the normal map also effects my cel shading which I'm trying to prevent. Instead of having a clean toon shadow, it creates more realistic shadows per the Normal Map (which is expected I guess since that's what Normal Maps are for lol).

    So my question is, how would I use the normal map to only apply a secondary moss texture without affecting the object's lighting? Is this even possible? I've pasted my moss toon shader below for reference but any insight on this is greatly appreciated! Thanks so much!

    Code (CSharp):
    1. Shader "Custom/ToonLitMoss"
    2. {
    3.     Properties{
    4.         [Header(Base Parameters)]
    5.         _Color("Tint", Color) = (1, 1, 1, 1)
    6.         _MainTex("Texture", 2D) = "white" {}
    7.         _MainNormal("Normal", 2D) = "bump" {}
    8.         [HDR] _Emission("Emission", color) = (0 ,0 ,0 , 1)
    9.  
    10.         [Header(Lighting Parameters)]
    11.         _ShadowTint("Shadow Color", Color) = (0, 0, 0, 1)
    12.  
    13.         // Moss Stuff
    14.         _SecTexture("Texture", 2D) = "white" {}
    15.         _SecColour("Colour", color) = (1, 1, 1, 1)
    16.         _Level("level", Range(-1, 1)) = 0
    17.         _Direction("Direction", Vector) = (0, 1, 0)
    18.     }
    19.  
    20.         SubShader
    21.         {
    22.             CGPROGRAM
    23.  
    24.             #pragma surface surf Stepped fullforwardshadows
    25.             #pragma target 3.0
    26.  
    27.             sampler2D _MainTex;
    28.             sampler2D _MainNormal;
    29.             sampler2D _SecTexture;
    30.             fixed4 _SecColour;
    31.             float4 _Direction;
    32.             float _Level;
    33.             fixed4 _Color;
    34.             half3 _Emission;
    35.             float3 _ShadowTint;
    36.  
    37.  
    38.             float4 LightingStepped(SurfaceOutput s, float3 lightDir, half3 viewDir, float shadowAttenuation) {
    39.                 //Amount of normals point towards the light
    40.                 float towardsLight = dot(s.Normal, lightDir);
    41.                 float lightIntensity = step(0, towardsLight);
    42.  
    43.                 // Add shadows for soft shadow directional light. Crisp them using step
    44.                 float attenuationChange = fwidth(shadowAttenuation) * 0.1;
    45.                 float shadow = step(0.1 + attenuationChange, shadowAttenuation);
    46.  
    47.                 lightIntensity = lightIntensity * shadow;
    48.  
    49.                 float3 shadowColor = s.Albedo * _ShadowTint;
    50.                 float4 color;
    51.                 color.rgb = lerp(shadowColor, s.Albedo, lightIntensity) * _LightColor0.rgb;
    52.                 color.a = s.Alpha;
    53.                 return color;
    54.             }
    55.  
    56.          
    57.             struct Input {
    58.                 float2 uv_MainTex;
    59.                 float2 uv_MainNormal;
    60.                 float2 uv_SecTexture;
    61.                 float3 worldNormal;
    62.                 INTERNAL_DATA //necessary for WorldNormalVectors method
    63.             };
    64.  
    65.            
    66.             void surf(Input IN, inout SurfaceOutput o) {
    67.  
    68.                 //sample and tint albedo texture
    69.                 fixed4 c = tex2D(_MainTex, IN.uv_MainTex);
    70.                 float3 normal = UnpackNormal(tex2D(_MainNormal, IN.uv_MainNormal));
    71.  
    72.                 // Secondary texture (moss or snow)
    73.                 fixed4 SecColour = tex2D(_SecTexture, IN.uv_SecTexture) * _SecColour;
    74.  
    75.                 //determines the direction and the amount of moss / snow on the object
    76.                 half secdot = step(_Level, dot(WorldNormalVector(IN, normal), normalize(_Direction)));
    77.  
    78.                 c *= _Color;
    79.                 o.Albedo = lerp(c.rgb, SecColour.rgb, secdot);
    80.  
    81.                 //o.Albedo = c.rgb;
    82.                 o.Normal = normal;
    83.                 o.Emission = _Emission;
    84.             }
    85.  
    86.             ENDCG
    87.  
    88.         }
    89.         FallBack "Diffuse"
    90. }
     
  2. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,352
    Replace:
    Code (csharp):
    1. o.Normal = normal;
    with
    Code (csharp):
    1. o.Normal = half3(0,0,1);
    The
    o.Normal
    needs to be assigned something, otherwise the
    WorldNormalVector()
    will silently break and just return the unmodiifed normal vector passed to it. And assigning
    half3(0,0,1)
    means it'll just use the original vertex normal for lighting, but keep
    WorldNormalVector()
    functioning properly.
     
    SomeHumbleOnion likes this.
  3. SomeHumbleOnion

    SomeHumbleOnion

    Joined:
    Jan 18, 2021
    Posts:
    28
    Perfect! Thanks so much friend this is exactly what I was looking for! :)