Cel shading is a technique to make 3D objects look 2D or flat. In 2D, you can make an object look 3D by applying a smooth gradient. However, with cel shading, you're breaking up the gradients into abrupt transitions. Typically there is only one transition where the shading goes from fully lit to fully shadowed. When combined with outlining, cel shading can really sell the 2D cartoon look.
// ...
float diffuseIntensity = max(dot(normal, unitLightDirection), 0.0);
diffuseIntensity = step(0.1, diffuseIntensity);
// ...Revisiting the lighting model,
modify the diffuseIntensity such that it is either zero or
one.
The step function returns zero if the input is less than
the edge and one otherwise.
// ...
if (diffuseIntensity >= 0.8) { diffuseIntensity = 1.0; }
else if (diffuseIntensity >= 0.6) { diffuseIntensity = 0.6; }
else if (diffuseIntensity >= 0.3) { diffuseIntensity = 0.3; }
else { diffuseIntensity = 0.0; }
// ...If you would like to have a few steps or transitions, you can perform something like the above.
// ...
diffuseIntensity = texture(steps, vec2(diffuseIntensity, 0.0)).r;
// ...Another approach is to put your step values into a texture with the
transitions going from darker to lighter. Using the
diffuseIntensity as a U coordinate, it will automatically
transform itself.
// ...
// ...
float specularIntensity = clamp(dot(normal, halfwayDirection), 0.0, 1.0);
specularIntensity = step(0.98, specularIntensity);
// ...Using the step function again, set the
specularIntensity to be either zero or one. You can also
use one of the other approaches described up above for the specular
highlight as well. After you've altered the
specularIntensity, the rest of the lighting calculations
are the same.
(C) 2020 David Lettier (lettier.com)
(C) 2026 Shivam Kumar