[Solved] ToonRamp Shader + Normal Maps: How to keep strict lighting bands?

I’m trying to achieve a shading similar to the one on this image:

Image 1: Reference - Album on Imgur

To get something similar to this I wrote a toon ramp shader (similar to the one in the Standard Assets) with normal maps. The shader works, and without a normal map you can see the lighting bands strictly defined, with visible seams separating them (Image 2). But once you add a normal map, the toon bands won’ t have well defined seams anymore, and will blend smoothly all across the mesh (Image 3).

Image 2 & 3: Shader - Album on Imgur

This is my current shader:

Shader "Custom/ToonRampWithNormals"
{
    Properties
    {
        _Color ("Color", Color) = (1,1,1,1)
        _MainTex ("Albedo (RGB)", 2D) = "white" {}
        _BumpMap("Bumpmap", 2D) = "bump" {}
        _Ramp("Toon Ramp", 2D) = "white" {}
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 200

        CGPROGRAM
        #pragma surface surf Ramp

        // Use shader model 3.0 target, to get nicer looking lighting
        #pragma target 3.0

        sampler2D _Ramp;

        half4 LightingRamp(SurfaceOutput s, half3 lightDir, half atten) {
            half NdotL = dot(s.Normal, lightDir);
            half diff = NdotL * 0.5 + 0.5;
            half3 ramp = tex2D(_Ramp, float2(diff,diff)).rgb;
            half4 c;
            c.rgb = s.Albedo * _LightColor0.rgb * ramp * atten;
            c.a = s.Alpha;
            return c;
        }

        struct Input
        {
            float2 uv_MainTex;
            float2 uv_BumpMap;
        };

        sampler2D _MainTex;
        sampler2D _BumpMap;
        half _Glossiness;
        fixed4 _Color;

        UNITY_INSTANCING_BUFFER_START(Props)
            // put more per-instance properties here
        UNITY_INSTANCING_BUFFER_END(Props)

        void surf (Input IN, inout SurfaceOutput o)
        {
            // Albedo comes from a texture tinted by color
            fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;
            o.Albedo = c.rgb;
            o.Normal = UnpackNormal(tex2D(_BumpMap, IN.uv_BumpMap));
            o.Alpha = c.a;
        }
        ENDCG
    }
    FallBack "Diffuse"
}

So, what have I done wrong? How can I have well defined lighting bands and have normal mapping?

If you can think of a better way to achieve something similar to the reference or have any thoughts about it please share, any contribution is much appreciated.

Is your ramp texture 4 pixels wide and using point sampling, or is it a wider texture with wide bands of solid color? And do you have mip maps enabled?

You can try enabling point sampling and disable mip map generation to see if that helps. I suspect it will a lot.

Next problem is your reference image has very simple shapes in the normal maps with very few fine details. Your example normal maps have a lot more detail, which means a lot of the toon shading is going to get effectively dithered making it appear “smoother”. Try blurring your normal maps, or running a high pass filter on it in your 2D art tool of choice.

1 Like

Hello bgolus, thank you for the swift reply, your postings are always super helpful.

By enabling point sampling you mean setting the ramp texture filter mode to point?

My ramp texture is 256x1 pixels. I changed the texture filter mode to point (it was bilinear and I completely forgot), and disabled mipmaps. But no visible change. And since you mentioned 4 pixels wide I also just made an 256x4 version but no visible change either.

BUT… The texture really had a bunch of detail. Tried a simpler one and BAM it works (see pic).

I really want to make this as best as possible, so since you are here, do you think my current approach will suffice to get a lighting similar to the reference? I don’t know much about shading and can’t tell if there is something other than normal mapping in the there.

I suspect your reference image isn’t using a ramp texture on the lighting, but rather limiting the color palette of the final color, but it’s hard to say for sure. You’ll certainly get something close to that reference.

Thank you very much bgolus!