DEV Community

Cover image for Cel (toon) shading
Graham Long
Graham Long

Posted on • Updated on

Cel (toon) shading

Starting with Phong Lighting

The beginnings of toon shading is implementing normal phong lighting, for this I followed this tutorial to add ambient, diffuse and specular lighting components to the AbstractGameObject shaders.
Before adding the shading I imported a toroid model to the scene as this was curved and would show off the lighting quite nicely, prior to any lighting this looked like:
toroid prior to shading


This is a minimum lighting that all pixels of a model will be subject to, initially I have set this to 0.3.


This is the lighting that hits a point on the model and is reflected in all directions, adding this gives a more realistic 3D shading effect especially on curved models.
The amount of diffuse lighting (fDiffuse) is calculated to be a value between 0.0 and 1.0 which is multiplied by the light colour.


This lighting is the light reflected from the source, off the model and directly at the camera.
The amount of specular lighting (fSpecular) is calculated to be a value between 0.0 and 1.0 which is multiplied by the light colour and the specular strength which I have set to 0.5 for now.

Applying these 3 lighting components produced a "realistic" looking toroid:
Toroid with Phong lighting
One issue with this is the fact that the lower half of the toroid is illuminated despite the fact that the upper half is between it and the light source directly above, I may look at this in future if I implement shadow maps.

Toon shading

Toon shading starts as phong shading, the only difference is that the calculated fDiffuse and fSpecular values are set to one of a finite number of levels.
To do this we calculate which level the value belongs to by multiplying it by the number of levels and flooring that value, once we have the level number we multiply that by the step change per level which is done by dividing by the number of levels, to simplify the process of applying this conversion I created a small function in the fragment shader which took in an intensity value and a number of levels and returned the resulting toon shading level, by adding the number of levels to the function parameters I can easily have more or less levels for either the fDiffuse or fSpecular values if needed, although for now I have kept them the same.

toonify function

One thing to note is I needed to change the fragment shaders float precision from mediump to highp to get the floor function to work.

Initially, I set the number of levels for both values to 3 and got this effect:
toon 3

Changing the number of levels for both values to 10 give a completely different effect which I think is too far.
toon 10

Reducing the number of levels to 5 gives a better effect but not quite what I am going for.
toon 5

Finally I decided on 4 levels which gives a nice compromise, as the value for number of levels is a float, I could set the number of levels to 3.5 etc but I think 4 is a good round value to start.
toon 4


Toon shading is a relatively simple change from standard Phong lighting techniques and provides a nice effect, Whether it is the final shading style that I go for is still to be decided.

The source code for this project at this point can be found here.

Discussion (0)