Behind the ‘toons, part 2: The Shader

Wanna know how I made this toon shader? Read on…
Final toon render

Toon Shader Basics

If you know the basics of a toon shader in Maya, feel free to skip ahead to Building Moom’s Shader.

A simple toon shader in Maya is a surface shader fed by a ramp containing two colors: The fill and the outline. This ramp isn’t driven by a regular texture placement node, but rather a SamplerInfo node.
Basic toon shader network (click to enlarge)

The SamplerInfo node has many useful properties, one of which is called facing ratio. This property indicates how directly a point on the object being rendered faces the camera. Here’s a definition from the Maya help files:

Facing Ratio gives you a number between 0 and 1 that tells you if the point being sampled is facing towards or away from the camera. A value of 1 means that it is facing the camera head-on. A value of 0 means that is is facing 90 degrees from the camera.

Facing Ratio explained

In a toon shader, the facing ratio of the SamplerInfo node is used to pick the color from the shading ramp. A point along the object’s edge is turned 90 degrees away from the camera, so its facing ratio = 0 and it gets the outline color. A point directly facing the camera has a facing ratio = 1, so it gets the fill color.
Basic toon shading ramp (click to enlarge)

Moom’s shader is built around this same basic structure, but adds a few bells and whistles along the way. Let’s take a look!

Building Moom’s Shader

In a nutshell, Moom’s shader is made from a soft, custom toon shader multiplied with a painted texture.
The complete shader network (click to enlarge)

Part A: The painted texture

The only reason I had to paint a texture here was to turn the inside of the mouth black, darken his lips and the soles of his feet. I felt it needed that contrast. Here’s the texture, superimposed on his UVs. Very basic, as you can see.
Painted body texture (click to enlarge)

Part B: The procedural shading network

The “soft toon shader” I mentioned earlier builds on the simple toon shader concept, but with a few interesting tricks. For one thing, instead of a hard toon line, I have a soft gradient on the shading ramp.
Final shading ramp
Since this part will be multiplied with the painted texture, most of this ramp is white. The multiply node in Maya works pretty much like the blending mode in Photoshop. Any color multiplied with white stays the same, so where this ramp is white, the final shader will look just like the painted texture. Where this ramp has color, the painted texture will get darkened and shaded by it.

Which brings us to the last part: How do I decide which color to pick from the shading ramp?

I’m doing this in two ways: With a SamplerInfo node, and with a little procedural NPR (Non Photo Realistic) network I built. Then I average the result of these two to get the final look (More on that in a bit).

We’ve already seen what the samplerinfo does; so I’ll just talk about what the procedural network is doing here.
The procedural shading network (click to enlarge)

It starts with a regular shiny Phong shader with a fractal bump map on it. The bump map’s job is to break up the shading and make it look rougher. It won’t really end up looking like a bump, as you’ll see.
The output of the Phong shader is clamped to a number between 0 and 1. Without getting technical about the clamp, I can say that I’m using it like “auto levels” in Photoshop: the lightest part of the shader is forced to become 1 (think of it as white), and the darkest part is forced to become 0 (black).
In effect, the clamp reduces the shading of my bumpy, shiny phong network to a simple number between 0 and 1. The samplerinfo node also provides me with the facing ratio which ranges from 0 to 1. The final trick here is to use the average of these two numbers to drive my shading ramp.

The Average Trick

While creating this shader, I first tried each of the two approaches by itself. The SamplerInfo node — which responds only to the facing angle — gave me a nice clean toon outline, but the result looked too flat and bland. On the other hand, the procedural network I made — which responds to the light in the scene — had some good rough texture, but its result looked too typically CG; too hard and plastic. By averaging both of these, I got the best of both worlds. See the images below.


shading_finalAnd that’s how it works! If you have any questions or suggestions, please leave me a comment. If you try out this shader on your projects, please share the results! I’d love to see what you come up with.

Other posts in this series:
Behind the ‘toons, part 1: Backgrounds


17 responses to “Behind the ‘toons, part 2: The Shader

  1. Amazing. Thanks for sharing! I have been playing with Maya’s ramp shader to create a similar effect but I like what you made much more.

    I am having a bit of a hard time with the technical stuff though, since I am unfamiliar with these utilities. If it is not too much trouble, I was wondering if you could help?

    I got something similar to what to what you have, but it seems a bit off. I wonder if I connected things wrong or perhaps I just need to adjust my ramp.

    I made a phong and put a bump on it, which was simple enough. Then I connected the phong’s outColor to the clamp’s input and set the min to 0 and max to 1.

    Then I figured I had to connect the clamp’s output to the Average utility, and it would only allow me to connect it to the input3D[0], so I did. I then also have the SamplerInfo’s facing ratio connected to the Average’s input1D[0], which again is all it would allow me to do.

    Then I connected the Average’s output1D to the ramp’s U and V Coordinates. Finally, the ramp’s outColor connects to the surfaceShader’s outColor.

    Sorry for the mouthful 😛 I would much appreciate it if you could help.

    Anyways, I am really interested in non-photorealistic rendering and animation, so when I saw your Moom animations I was quite excited. Just wanted to say thanks for putting your stuff out there. Keep up the awesome work 🙂

  2. @Xuco: Aah yes, making connections in Maya sometimes acts weird. For the average node, both the inputs must be connected to the same type of input – either input1D or input3D. Your clamp goes to input3D while your facing ratio goes to input1D. So you’re not getting the right average.

    In this case, since we’re dealing with simple numbers, we use input1D. But it may not allow you to make the exact connections you want! So here’s where MEL helps.

    Let’s say your clamp node is called clamp1, and your average node is called avg1. Use the following MEL command to make the right connection:

    connectAttr -force clamp1.outputR avg1.input1D[1]

    The “-force” option forces the connection.
    Since your SamplerInfo’s facing ratio is connected to input1D[0], now both your nodes will be connected to the right inputs, and the averaging will work fine.

    As for your Ramp, check if it’s a U or V ramp, and connect the output of the average to the appropriate co-ordinate. I have a U ramp, and the “output1D” of my average node is connected to “uCoord” of my ramp.

    Try these changes, and it should work!

    Thanks again for the kind words. 🙂

  3. Awesome. That got it working. Thanks, Sunny! I’m gonna do some tests and see what I can come up with. Thanks again 🙂

  4. oh great! I’ve been waiting for some hints on how to achieve the look!

    i’ll let you know how my results come out and any tweaks I make.

    Cheers and thank you.

  5. hey Sunny.

    just a few small questions.

    would mind explaining your lighting set up? is it just one directional light coming from the direction of the camera or up above?

    do you turn off receive shadows on the model.

    also, are you going to go over the tweaks between this shader and the one from the bee chase?

    I tested out the shader on a grim reaper character, not getting the greatest results, but it’s an exploratory process!

    cheers and thanks again.

  6. @Tyler:
    Great questions! I should have addressed the lighting and shadows in my post, but here goes:

    I’m using a single directional light, coming from behind the camera, pointed slightly down.

    Both “cast shadows” and “receive shadows” are ON for the character. Without them the shading looked really flat. The reason you don’t see Moom’s shadow on the floor is that he was rendered separately, in his own render layer.

    In the Bee Chase scene, I did render his shadow, but even that was a separate layer that was comped in AE. Gave me more control over the darkness of the shadow that way.

    The shader in the Bee Chase scene is identical to this one. However, I did post-process it differently while compositing. The render looked too hard-edged, so I softened it in AE by duplicating the layer, applying a slight blur on the top copy and playing with the blending modes.

    Try the lighting and shadow attributes on your Grim Reaper. Feel free to post an image on your blog and send me a link if you’d like any feedback. Love those trees btw! Great flowing shapes.

    @Leon, Samyuktha:
    Thanks! Let me know if you try it out.

  7. Pingback: LABtrazos » Blog Archive » Crear cristales en Maya.·

  8. Hi, maya newbie here…the final result of this process looks awesome! Iwant to try this, but I have no idea about making connections in maya:/ Can you please simplify the above process in a step by step method? For example applying this effect to a simple nurbs sphere or a polygon cube? Your help will be greatly appreciated 🙂
    – Cheers!

  9. Hi Hiro! I found this other tutorial on making a simple toon shader that will help you. If you need to start with the basics of making connections, check the Maya help and search for Hypershade, which will go over the basics in detail. Thanks for the comment, and hope this helps!

  10. Thanks! that tutorial was helpful and straightforward! can you please explain how did you connected the clamp and average utilities to the shader?

  11. I’m using the regular Maya renderer here. As for the clamp and average: I connected the outputR of the clamp and facingRatio of the samplerInfo to the two inputs of the Average node, and then the output of the average node to the uCoord of the ramp. The details of the connections have changed in the new Maya version, so definitely check out the help on all these nodes to make sure you have the right inputs and outputs.

  12. Pingback: boBlog·

Comments are closed.