Toon Shading with Unity Shader Graph

For a long time, I wanted to create a toon shader with this cool multi-step shading. Such effect was used for example in that small game called Brawl Stars.

So how can one create such an effect himself? As always, there are a few steps to follow.

Create a graph ?

The first step will be, of course, to create a new shader graph. For our purpose, we have to use the Unlit Shader Graph.

Create a new shader graph.

Let’s call it “Toon Shading Shader Graph” and open it.

Solving Light ?

The next step is adding shading to the unlit shader. To do it, we have to make a simple calculation. It will be a dot product of the normal vector of our mesh and light direction.

To get a normal vector in world space, we have to use the Normal Vector node with the World option selected. We can connect it to the Dot Product node.

Connecting Normal Vector and Dot Product nodes.

You may notice that there is no node to get light in the Shader Graph. But that won’t be a problem!

We can use a Custom Function node. We can define its inputs and outputs as well as the code of that function.

Here we will need 2 outputs: Direction (Vector3) and Color (Vector3). The first one will be used in our Dot Product, and the second one will be used to tint the model.

Next, we have to define the body of our function. In type, please select “String” as we just want to paste our code to the node. Otherwise, we can write it to the file, but we won’t do that this time.

Our code will grab the main light for our model, and we can simply read its properties.

	Direction = half3(0.5, 0.5, 0);
	Color = 1;
	Light light = GetMainLight();
	Direction = light.direction;
	Color = light.color;

You should end up with something like that:

Custom Function node for getting main light.

Now we have to connect the “Direction” to the Dot Product node.

Solving light direction for our model.

If we connected the Dot Product’s output to the Color of Unlit Master node, we would see which part of the model is lit.

Result of Dot Product on the model.

Add shading ?

The value of the dot product ranges from -1 to 1, which we can use to add more depth to our shadow. To do that, we will use a ramp texture.

Ramp texture is nothing more than a texture with a height of 1px and a width of how much you want. In our case, I made a simple 1×16 texture with a different amount of shading.

Ramp texture for shading.

To use it in our shader, we have to add a property, which I’ll call “RampTexture”, and it’ll be Texture2D type.

Ramp texture property.

Now, we will have to normalize the output of the Dot Product to be in the range of 0 to 1. To do it, we just need to add 1 to the value and later divide it by 2.

Normalize dot product.

When we have that, we have simply read the ramp texture on X-axis.

Reading Ramp Texture with Dot Product.

Now let’s multiply that by the light color, and we should have some promising results.

Multiply Ramp Texture and Light Color.
Shadows are getting darker!

Let’s add actual texture to the model ?

Our model is currently missing texture, and I think we should do something about it.

Add another Texture2D property to the shader. It will be the “Base” texture for our model.

Base texture property.

To connect that texture with our current value of the shadow, we can just multiply the value from the texture sample and the result of the ramp and light multiplication.

This should combine both values together and make our model look pretty. ?‍♀️

Sampling base texture and combining it with the graph.

Ready to see the result?

Result ?

Toon shading completed!

We did it! And I really like the result!

But it also has some problems… Even if we can make it work with Shader Graph, it’s still missing a few things. For example, we can’t make it receive shadows from other objects… ?

So, in the end, we might still have to code it at some point… But that’s for another time ?

If you want to learn more, I would highly suggest taking the time and visiting these pages:

Other than that, let me know what you think about it in the comment section below!

If you know someone that might need this, share it with him! I would really appreciate that! ?

And if you are interested in getting emails when I release a new post, sign up for the newsletter!

The whole project is available at my public repository. ?

Hope to see you next time! ?

4.7 7 votes
Article Rating
Notify of
Newest Most Voted
Inline Feedbacks
View all comments
3 years ago

Awesome Thanks a bunch…

3 years ago

if you have the error “Invalid conditional expression”, change

Andrea Leganza
Andrea Leganza
2 years ago

You can also replace the ramp texture with a Gradient (colors set to Mode: fixed) + Sample gradient, the Time input of the sample gradient will be the output of the Vector 2.

1 year ago

I’m having this error:’MainLight_float’: output parameter ‘Direction’ not completely initialized

Would love your thoughts, please comment.x