Non-photorealistic rendering
Cel shading builder
Cel shading is not a different lighting model — it is the ordinary diffuse term with the smooth ramp thrown away. Start from a normally shaded object on the left, then chop its N·L ramp into flat bands. Drag the band edges on the ramp strip to see exactly where each boundary lands on the surface.
Every value of N·L between 0 and 1 maps to its own brightness, so the terminator between lit and unlit is a gradient many pixels wide.
The same N·L is snapped to the nearest band. The terminator collapses into a hard line, which is what reads as "drawn" rather than "rendered".
The top strip is the smooth ramp, running from fully unlit at the left to fully lit at the right. The bottom strip is the ramp your shader actually samples. In a real engine this is a 1D texture — the fragment shader computes N·L, uses it as a texture coordinate, and reads back whatever colour the artist painted there.
Move the light and watch the band edges travel across the surface as hard curves. Those curves are contour lines of N·L — the geometry is unchanged.
float d = max(0.0, dot(N, L));, then either step through thresholds or use d as a texture coordinate into a 1D ramp texture. The ink outline is normally a second pass — either the back faces drawn slightly expanded, or the dot(N, V) test this demo uses.