Today, here’s my first batch of shaders from my Shader Journey – some basic toons!
This article is also available on Medium.
A few days ago, I introduced my latest CG series: “Shader Journey”. Throughout this series of articles, I will detail my adventure in the world of shaders! And to kick it off, let’s start with some toon-styled renders 🙂
A quick overview
In this episode, I’ll talk about 4 shaders: the Toon Diffuse, the Toon Basic, the Toon “2 Shadows” and the Toon Fresnel. Those are shown in the video above, and here is how they look on various shapes:
Because I’m working on toon shading, it’s much prettier (and more useful, usually) with round or soft objects, so I’ll focus on those and I won’t talk about the (questionable?) results of my shaders on cubes… 😉
Together, these 4 shaders combine all of these features:
- toon shading
- diffuse + specular lighting (1 directional light)
- 1- or 2-steps shadows
- specular intensity
- gloss size
- basic gloss/shadow anti-aliasing
- Fresnel (with customisable intensity, colour and pulse speed)
Note: you can see how the colour/intensity of the light source affects the material at the end of the animation, when it all goes dark blue…
So, are you curious to see how I made them? Then, let’s dive in! 🙂
In CG, the toon style is a specific rendering style that draws inspiration from the hand-drawn comics. The main difference with a classical rendering style is that shadows are not gradients of continuous tints, but rather the surfaces are divided in a given number of brightness levels.
For example, here is a sphere with a simple diffuse lighting in “classical” and “toon” styles:
As you can see, colours are more “harsh” in toon shading: basically, you get rid of all the intermediate values and you simply cut your surface in two parts around a threshold.
This type of render reminds the viewer of comic books, animes and other “cartoony” media; it can also be used to get a “soft and cute” visual style (here are some examples from Dribbble.com):
The number of brightness levels can vary, depending on the exact effect you want to achieve, but in lots of video games and cartoon renders, you have just one or two thresholds.
Oftentimes, this toon shading is also used with specific character design (like the anime-styled big-eyed heroes), low-poly modelling or outlines. I haven’t worked on outlines yet, but I definitely plan to study those in the future… 😉
Shader n°1: Toon diffuse
The first thing I needed to get working was obviously this threshold logic. But that first meant preparing a basic lighting logic with more classical algorithms.
Making a diffuse lighting shader with Lambert
I started from a basic Lambert-based shader that basically gives you the kind of diffuse lighting we see on the picture above, on the left. This type of lighting does not depend on where your camera is, it works regardless of the viewer’s angle of view.
At that point, I’m only implementing very simple lighting algorithms and, in particular, I’m only taking care of one primary directional light source. This means that I suppose I have a sun that is infinitely far away from my objects and therefore casts parallel rays everywhere in my scene.
With that setup, the idea of Lambert-based diffuse lighting is to simply get the dot product between the light vector of the primary directional light source and the normal of your surface at the point of computation.
Because of how dot product works, this directly gives a very high value (1) when the vectors are parallel and a very low value (0) when they are orthogonal; this directly maps to the physical phenomenon we are trying to approximate: if the light source is looking at the surface directly, then the surface is very lit and we see a lot of colour, else the surface is dark and we mostly see black.
We get this reflectance value for each fragment of our image and this gives us the “Lambert mask”, the grayscale mapping of low and high light intensities:
Finally, all we have to do is multiply this reflectance value by the colour of our object, and by the colour of the light source, and we get a basic diffuse light shading 🙂
Getting the toon effect with a threshold
To get a toon shading, things are pretty similar except that I need to threshold the Lambert reflectance mask.
To do this, I can use a GLSL function called
step() that simply returns 1 if you’re above the specified threshold or 0 otherwise. This is perfect for our threshold computation!
I simply give the user-defined threshold ratio as a parameter to the
step() function, and apply it on my Lambert mask to turn it into a 2-colours grayscale mask, with just white (high-intensity) and black (low-intensity):
And at that point, multiplying by the object and light colours directly gives me my toon-diffuse lighting!
Adding some anti-aliasing
Something that annoyed me when I first used this
step() function was that, of course, it gives you a pixel-perfect threshold… meaning that you get a visible “cut”. With a pixelated line that has a lot of aliasing…
This depends on the graphics settings of your Unity project, but it can quickly spoil the fun. So I also added a little parameter, the “diffuse smoothness”, and I replaced my
step() with a
smoothstep(): this allows me to very slightly blur the dividing line and add a quick antialiasing of sorts.
Note that it is a bit exaggerated in my first screenshot, at the top of the article, to show its effect in the shader – in truth, you would have to adapt this “smoothness” to the shape of your mesh to still keep the toon effect and avoid going back to a shadow gradient…! 🙂
Colouring the shadow
One last thing I wanted for my base shader was to better handle the colour of the shadow. So far, I’ve only multiplied my colour by the Lambert mask, so the parts that are looking away from the light and that are dark are… well, very dark.
And also: I can’t control the colour of this shadow other than by changing the colour of the object itself. The two are usually linked but it can be interesting to have more control for some specific styling effects, with a shadow that has a completely different colour due to environment:
Now, this is absolutely not physically accurate but it can be a quick way of testing out some strange ambiances 😉
So, to control this shadow colour, I added a “shadow colour” parameter in my shader and then mixed two values to get my final shadow colour:
- this user-defined colour that is passed through the
- and a very dark equivalent of the object’s base colour
This way, if the user keeps the default value for the “shadow colour” parameter that is black, the shader will still give a little bit of colour to the shadow instead of making it full black. This avoids having a too-large black area on-screen; because remember that, with toon shading, we don’t have gradients and continuous values so we would just get half a screen of black pixels:
By adding this non-physically accurate “inherent lighting”, I get a more visible shape but still an overall valid picture to the eye:
Shader n°2: Toon basic (with specular lighting)
To improve my initial shader, I decided to add the second type of lighting we usually have for materials in CGI: the specular lighting. This is what makes the little shiny spots on plastic or metal in your renders:
Contrary to diffuse lighting, specular lighting depends on your view angle. This glossy white spot on the surface moves around as the light or the camera’s positions change.
There are various ways of getting those reflections and, nowadays, physics-based rendering does some amazing stuff at simulating very photorealistic materials! But I’m still warming up, so I’m sticking with the “old way” of computing lighting for now, and I’m doing BRDF shaders.
For specular lighting, this means I can basically choose between two algorithms: the Phong or the Blinn-Phong. Strangely enough, the second one involves some weird computation but it works better in practice, so that’s the one I’ve implemented 🙂
I won’t detail the implementation too much (feel free to check out the Github repo for the full code 🚀), computing the Blinn-Phong reflection basically has you compute another dot product, this time between the normal of your surface and the half-vector which is derived from the view vector.
Turning it into a toon shader was once again about adding steps to threshold the specular mask and turn it into a crisp spot on the surface:
I also added a little “specular smoothness” parameter to help with the antialiasing, like before on my shadow threshold 🙂
Shader n°3: Toon “2 Shadows”
As I said in the introduction of this article, basic toon shading usually uses one shadow, but you can actually define several thresholds on your surface to get more than 2 brightness levels. The more levels you add, the closer you get to a “classical” continuous lighting shader.
Here, I decided to go for a second shadow, so this means a second threshold.
Most of the computation is the same but, this time, instead of making just one, we can actually compute two Lambert reflectance masks (one with the smaller shadow threshold and another with the larger shadow threshold) and then compute the difference between the two to get the middle band mask.
This middle band receives the “light shadow” colour, while the rest of the mask is used for the “dark shadow”.
The specular lighting is not affected by this new threshold, so the rest of the computation works exactly the same, and we get our brand new “2 Shadows”-toon shader! 🙂
Shader n°4: Toon Fresnel
Because Fresnel is cool! 🙂
Ok so – the Fresnel effect is a complex physical phenomenon, but as explained by Dorian Iten in this really nice article, in computer graphics it can be summed up as: “the more shallow the view angle of the surface, the stronger the reflection”.
For example, that’s why the surface of a lake is transparent near the shore but almost mirror-like in the distance:
The Fresnel effect has some impact on the transparency and reflectiveness of the material. For round objects, it also makes the specular reflections more intense on the edges.
So this means that by adding Fresnel on our objects, if they have soft edges, we’ll get some “glowy” effect on the border – in my shader, I’ve added a parameter to tint the Fresnel with a user-defined colour:
The Fresnel intensity can be computed quite easily just by doing yet another dot product, this time between the view vector and the normal of the surface.
And we can even play around with Unity’s built-in time variables for the shaders and have this Fresnel “pulse”: this can be a really nice trick to show if an object is selected or pointed at, for example 🙂
This first batch of shaders was really interesting to make!
It allowed me to revisit basic lighting algorithms (the Lambert and the Blinn-Phong) for diffuse and specular, but with a little additional twist: the implementation of thresholds to get discrete brightness levels and a toon shading.
I hope you like this project so far – and as always, feel free to react in the comments if you have ideas of effects or shaders I could try 😉