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

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!

Shader "Custom/ToonLitMoss"
{
    Properties{
        [Header(Base Parameters)]
        _Color("Tint", Color) = (1, 1, 1, 1)
        _MainTex("Texture", 2D) = "white" {}
        _MainNormal("Normal", 2D) = "bump" {}
        [HDR] _Emission("Emission", color) = (0 ,0 ,0 , 1)

        [Header(Lighting Parameters)]
        _ShadowTint("Shadow Color", Color) = (0, 0, 0, 1)

        // Moss Stuff
        _SecTexture("Texture", 2D) = "white" {}
        _SecColour("Colour", color) = (1, 1, 1, 1)
        _Level("level", Range(-1, 1)) = 0
        _Direction("Direction", Vector) = (0, 1, 0)
    }

        SubShader
        {
            CGPROGRAM

            #pragma surface surf Stepped fullforwardshadows
            #pragma target 3.0

            sampler2D _MainTex;
            sampler2D _MainNormal;
            sampler2D _SecTexture;
            fixed4 _SecColour;
            float4 _Direction;
            float _Level;
            fixed4 _Color;
            half3 _Emission;
            float3 _ShadowTint;


            float4 LightingStepped(SurfaceOutput s, float3 lightDir, half3 viewDir, float shadowAttenuation) {
                //Amount of normals point towards the light
                float towardsLight = dot(s.Normal, lightDir);
                float lightIntensity = step(0, towardsLight);

                // Add shadows for soft shadow directional light. Crisp them using step
                float attenuationChange = fwidth(shadowAttenuation) * 0.1;
                float shadow = step(0.1 + attenuationChange, shadowAttenuation);

                lightIntensity = lightIntensity * shadow;

                float3 shadowColor = s.Albedo * _ShadowTint;
                float4 color;
                color.rgb = lerp(shadowColor, s.Albedo, lightIntensity) * _LightColor0.rgb;
                color.a = s.Alpha;
                return color;
            }

          
            struct Input {
                float2 uv_MainTex;
                float2 uv_MainNormal;
                float2 uv_SecTexture;
                float3 worldNormal;
                INTERNAL_DATA //necessary for WorldNormalVectors method
            };

           
            void surf(Input IN, inout SurfaceOutput o) {

                //sample and tint albedo texture
                fixed4 c = tex2D(_MainTex, IN.uv_MainTex);
                float3 normal = UnpackNormal(tex2D(_MainNormal, IN.uv_MainNormal));

                // Secondary texture (moss or snow)
                fixed4 SecColour = tex2D(_SecTexture, IN.uv_SecTexture) * _SecColour;

                //determines the direction and the amount of moss / snow on the object
                half secdot = step(_Level, dot(WorldNormalVector(IN, normal), normalize(_Direction)));

                c *= _Color;
                o.Albedo = lerp(c.rgb, SecColour.rgb, secdot);

                //o.Albedo = c.rgb;
                o.Normal = normal;
                o.Emission = _Emission;
            }

            ENDCG

        }
        FallBack "Diffuse"
}

Replace:

o.Normal = normal;

with

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.

1 Like

Perfect! Thanks so much friend this is exactly what I was looking for! :slight_smile: