Eevee Toon Shaders

“Today we have a lengthy guest post from Wasili & Marius. Toon shaders setup in Eevee!” [Light]

Getting started

For a long time, the 2.8 project has been on everyone’s mind, because of the announcement of something similar to Maya’s viewport 2.0. With a very large part of the blender community still only rendering on their own hardware, the prospect of a potential 60x (2 seconds vs 2 minutes per frame for example) render speed increase, is very attractive.

Wasili Novratidis attended the Blender conference and spoke to Ton and various developers about the prospects for NPR in Blender, with Eevee obviously being written with PBR in mind (the current industry trend). Ton mentioned the idea of an NPR engine for Blender at a later stage, but this would be a post-Eevee endeavor and we need something that can work with Eevee asap.

With the developers hands extremely full and no one else to really turn to, the work was left up to the NPR community. For a while, there have been some pretty stunning work on shader adjustments by Raul Arturo Gonzalez and Paul Caggegi and an attempt by Marius Oberholster in Eevee in it’s extremely early stages, in a push towards getting the NPR tools that we are all looking for.

Around the time of Wasili’s report of the 2017 conference, he contacted Marius to see whether they could somehow come up with a solution for Eevee and the Cycles viewport. Marius’ focus was more leaning toward Eevee, because of Cycles’ impractical and tested render times (2x+ slower and less flexible than Blender Render on his PC i7, 550Ti), so work began on this “over their heads” task of attempting to build a shader that would make NPR a reality for Eevee.

It grew up so fast

Wasili kicked off things by building a very good diffuse shader (mainly two tone, but was colorramped so it could go to any amount). This was built on the emission shader, because it gives you the closest thing to a shadeless material, similar to what we had in Blender Render.

This, though giving a great look, was not compatible with any lights in the scene, didn’t have any self-shading and the light source still had to be tracked using drivers. This was a fantastic start, because Marius didn’t have a clue about vector math at this point and he learned a lot through this process too.

While we now had the beginnings of a good toon shader (despite the emission issues). We still had a mammoth in the room – no specularity! And it was really obvious. Unfortunately, specularity does not function like diffuse lighting does, because it is a lot more reflective in nature (you can see that in the formula explanations about what values mean what). You could say it is the relationship between the light source location, the object’s surface and the location your eyes. This required a lot more work than either of us thought!

How it works – General:

This is just a blanket statement to say that it is a shader built within a shader. We calculate the general properties we would need for a typical toon shader (which can be softened for other NPR uses too) and put that into the Color of the Diffuse BSDF (the principled shader is too heavy at this stage). Normals of the shader are also adjusted to give that flat lighting look.

How it works – Point lamp:

In the throws of trying to figure these things out, Wasili sent Marius a link on the Phong specularity model. Marius was led to build the formula in the node system and this led to some very interesting results in the beginning – even something that resembled anisotropic specularity that could be rotated via the mapping node (more on that later). Unfortunately, with nodes at least, we can’t get away from having to track the light source’s location using drivers. This means only a single light source on import, though you can add more fairly easily.

The biggest issues came with normal transforms, including camera location (eyes in the formula) and initially we tracked this as well. Thankfully, there is a node that has camera data (including location) and this allowed the specularity to even be influenced outside of the camera view (though pretty broken in that sense – it’s only for in-camera views at this stage).

For the specularity to work beyond the world origin point, we also had to add vector transform nodes to make all the normals calculated be local to the shape that the material might be on: World to Object.

The specularity now built and changing with camera motion, was still visible when the light source was directly behind the object. This is very similar to rim light, but not accurate since the light can’t technically reach there. There is a max in the formula to prevent this visibility behind the shape, but did not work as it was built. The solution for this was to mask it with a larger diffuse than the default on the shape (proper masking for this should happen for non-metallic materials because of shadows, but you have to tell a computer to do that otherwise it won’t).

The rest was fine tuning values and setting up a UI to make it easier to use. The great part is that as Eevee is in it’s current state, you can do almost anything and it will play nice with the viewport effects.

How it works – Sun lamp:

The sun lamp was calculated very similarly to the point lamp, except that the light location was added to every shape’s location to make it come from the exact same point. You could say the shader calculations assumes every shape is in the origin of the scene and shines the light and specularity accordingly with a few empties to allow you to rotate only the sun and affect the shapes correctly.

This did come at a cost though. To get flat shading, the sun causes a fade out of the material brightness in relation to the horizon (it’s normals seem to rotate as the sun does). The lower the sun sets, the darker the material gets. While physically accurate, it would be great to get a neutral response like on the point lamp model, while still being flat – similar to the point lamp.

License: CC0 – Public Domain

Of course we can’t claim this for ourselves, because it will prevent this from growing the way it needs to. We need these and you need these. For them to be improved and find their way into master at some point, others need to know how they work so they can also take it further than we can at this point. Others can bring more skill than we have to the table and now they have a reason to.

Additional features:

– Normal and bump mapping

This was added fairly close to the end of the process. In fact, it was the very last feature added – after the UI. While Marius is not personally a fan of normal mapping on toon shaded objects, he knew there were uses for it and was led to add it as well (makes more sense for smoother NPR styles).

At first, the general idea was to add the normal map to the current normals, but this is not going to give you the best results. You have a very set space where the normals respond and this caused a massive flattening on add or a huge crumpling on subtract. The answer here was to average it with the existing normals. Of course, strength can be increased by addition and so on, but in general, average should work.

Bump mapping was vectorized by putting the same gradient value for X,Y,Z. This gave a vector output and that is what can be mixed with the already averaged normal map.

The best about choosing average as the mix? You can:

  • turn them off completely
  • you can have both running on the same material
  • ¬†you can use whichever you prefer

– Ambient light

Just the diffuse color (not the diffuse shading) added using a mix and emission shader. Very useful for per-material environment lighting. Very useful for abstract scenes.

– Texturing

Just like any other shader, the diffuse can be textured using the standard coordinates and mapping nodes and so on. It also allows you to texture your specularity – a very useful feature for extremely stylized NPR.

– Doesn’t have to work alone

A great addition, though unintended, is that the mixes really well with transparency and other shaders. Just like in the tutorial video, you can see that it can be reflected and can be mixed with glass and other shaders quite well.

– Wish List features now possible

Sharpness control for the UI section. This would pertain to:

  • Diffuse
  • Diffuse Mask
  • Specularity

Anisotropic-like shader. Currently functional, but has a few more adjustments (like those mentioned above). We’d like to include it with an update to the rest.

Difference between Phong and Blinn:

The biggest differences come down to how it is calculated. While Phong has normalization built into the formula, Blinn does not and did require normalization to not be super sensitive to any adjustments (especially size). Blinn is also much simpler and easier to build than Phong, so it was less prone to breaking during the making of the shaders.

Phong is also much more sensitive than Blinn and decreases in size (and is also supposed to increase in brightness) with the approach of a light source. Blinn, while very closely resembling Phong’s calculations, is much more static and does not vary much based on light proximity – making it a generally more favored model in general for that stability.

Both contain the exact same diffuse model:

Diffuse = (N.L)

Current Limitations:

  • Only a single light source on import (can be fairly easily expanded, but Eevee may crash, because it is still very unstable and neither of us have made a multi-light source example yet).
  • Material goes out on certain angles with the sun lamp (horizon based)
  • The phong model specularity can be extremely sensitive and vanish easily (can be retrieved with increasing it’s size). For the sake of options and variety it was included.
  • Out of camera brokenness. Thankfully it is rarely that out of camera is what you want to render, so this shouldn’t be a problem in most cases.

So what is where in the node system?

To help with this predicament, we have put together node maps that have some short descriptions added to help you locate the various components in the setups. For those of you who are looking for clean maps, you can simply use those without descriptions added.

Simply stated, the equations were built as they were written (we truly did our best). This means that, following the headers and frames (as well as the description maps), you should be able to know what part of the equations are where.

The Vector Math node does not have all the general functions you would expect from the math node. This means that for even simple things like divide and multiply, we needed to split the vectors and calculate them per axis using math nodes. The math node cannot be directly used with vectors, because it gives only a grayscale output (single axis or single set answer). Vector math requires 3 (X,Y,Z).

We’re sure the developers have good reasons for only adding those which they did, but thankfully, some of the splitting should be toned down in a hopefully soon update, with Wasili’s coding of the multiply and divide functions – up for second review last checked.

Why Eevee?

Render times

While it is a fairly optimistic statement, Eevee presents an extremely large reduction in render times for users. With a simple test case, Marius got somewhere between a 30x-60x decrease in render times (with OpenGL render a few years ago – 2-4 s/frame instead of 1-4min/frame). That is huge potential!

Marius Oberholster – Blender Viewport Experiment
It Came Upon The Midnight Clear (29 Aug 2015)

We do still need a proper compositing system (will come later in the year hopefully) and we also still need an outline tool – these will also slow down render times a touch, so be realistic for your machine and project requirements.

For some examples of what you can do with this, you can see demo’s at the download website:
(You use these entirely at own risk)

There you will also find a tutorial on how to use them, more on why and blog posts relating to these.

Cycles caution

It does hold advantage for Cycles in giving you a more flexible NPR option, but since these were not directly built for and tested in Cycles, results will certainly vary. In one case, the sun lamp’s normal response was flipped by 90′ and the lamp size to shadow smoothness differs between Eevee and Cycles. They are not quite the same and don’t respond the same.

Personal note from Wasili:

Have been a rough, rowdy ride. Still with a good result. And likewise I could not have come this far without Marius. The toon shader would have simply been stuck with a lot of issues. Never the less I hope this will get NPR better on the roadmap. Have a lot ongoing a.t.m. and hope to bring nice results in 2018.

Personal note from Marius:

Of course, I can’t take the credit for this. Wasili laid a very solid foundation (especially with the diffuse and Blinn model) and helped a lot with the math side of things (providing suggestions as well as the formulas). For example, he built the diffuse, the initial Blinn model and helped with many edits and testing throughout the process – the perfect teammate for this project! Most of all, I have to thank GOD for this, because there can’t be any doubt that this project was so far above our playing field, it would have been impossible for us to make this on our own. I am extremely grateful beyond words that I could’ve been part of this process so far and I hoped these help out in a big way!