Good morning! I would love to use the Unity official toon shader in my unity 6 project. Are there any plans to have it upgraded soon to work with Unity 6 HDRP? The last supported version seems to be 2023.2.
Toon shading (often called cel shading) is a rendering style designed to make 3D surfaces emulate 2D, flat surfaces. This style entered the mainstream with games like Jet Set Radio and The Wind Waker.
This tutorial will describe step-by-step how to write a toon shader in Unity. The shader will receive light from a single directional source, and have specular reflections and rim lighting. We will use the art style of The Legend of Zelda: Breath of the Wild as our reference, although we will not implement some of the more complex rendering techniques used in the game.
When writing shaders in Unity that interact with lighting it is common to use Surface Shaders. Surface shaders use code generation to automate the object's interaction with lights and global illumination. However, as our shader will only interact with a single directional light, it will not be necessary to use surface shaders.
The first line requests some lighting data to be passed into our shader, while the second line further requests to restrict this data to only the main directional light. You can read more about Pass tags here.
To calculate our lighting, we will use a common shading model called Blinn-Phong, and apply some additional filters to give it a toon look. The first step is to calculate the amount of light received by the surface from the main directional light. The amount of light is proportional to the direction, or normal of the surface with respect to the light direction.
The normals in appdata are populated automatically, while values in v2f must be manually populated in the vertex shader. As well, we want to transform the normal from object space to world space, as the light's direction is provided in world space. Add the following line to the vertex shader.
Right now our line of code above using the ternary operator is a step function with two steps, light and dark. To render more than two bands of shading, we will need a function with more than two steps. The simplest way to implement this is with a ramp texture.
This looks good, but the dark side is too dark; right now it is completely black. Also, the edge between dark and light looks a bit sharp, but we'll deal with that later. For now, we will add ambient light.
Ambient light represents light that bounces off the surfaces of objects in the area and is scattered in the atmosphere. We will model it as a light that affects all surfaces equally and is additive to the main directional light.
This is called a property attribute. Colors normally have their RGB values set between 0 and 1; The [HDR] attribute specifies that this color property can have its values set beyond that. While the screen cannot render colors outside the 0...1 range, the values can be used for certain kinds of rendering effects, like bloom or tone mapping.
We multiply our existing lightIntensity value and store it in a float4, so that we include the light's color in our calculation. _LightColor0 is the color of the main directional light. It is a fixed4 declared in the Lighting.cginc file, so we include the file above to make use of the value.
Before going further, we'll soften the edge between light and dark to remove the jaggedness. Right now, the transition from light to dark is immediate and occurs over a single pixel. Instead, we'll smoothly blend the value from one to zero, using the smoothstep function.
smoothstep takes in three values: a lower bound, an upper bound and a value expected to be between these two bounds. smoothstep returns a value between 0 and 1 based on how far this third value is between the bounds. (If it is outside the lower or upper bound, smoothstep returns a 0 or 1, respectively).
smoothstep is not linear: as the value moves from 0 to 0.5, it accelerates, and as it moves from 0.5 to 1, it decelerates. This makes it ideal for smoothly blending values, which is how we'll use it to blend our light intensity value.
Specular reflection models the individual, distinct reflections made by light sources. This reflection is view dependent, in that it is affected by the angle that the surface is viewed at. We will calculate the world view direction in the vertex shader and pass it into the fragment shader. This is the direction from the current vertex towards the camera.
We'll now implement the specular component of Blinn-Phong. This calculation takes in two properties from the surface, a specular color that tints the reflection, and a glossiness that controls the size of the reflection.
The strength of the specular reflection is defined in Blinn-Phong as the dot product between the normal of the surface and the half vector. The half vector is a vector between the viewing direction and the light source; we can obtain this by summing those two vectors and normalizing the result.
We control the size of the specular reflection using the pow function. We multiply NdotH by lightIntensity to ensure that the reflection is only drawn when the surface is lit. Note that _Glossiness is multiplied by itself to allow smaller values in the material editor to have a larger effect, and make it easier to work with the shader.
Rim lighting is the addition of illumination to the edges of an object to simulate reflected light or backlighting. It is especially useful for toon shaders to help the object's silhouette stand out among the flat shaded surfaces.
The "rim" of an object will be defined as surfaces that are facing away from the camera. We will therefore calculate the rim by taking the dot product of the normal and the view direction, and inverting it.
With the rim being drawn around the entire object, it tends to resemble an outline more than a lighting effect. We'll modify it to only appear on the illuminated surfaces of the object.
As a final step, we will add the ability for our shader to cast and receive shadows. Shadow casting is very simple. Add the following line of code below the entire Pass (outside the curly braces).
In order to receive shadows, we will need to know in the fragment shader whether a surface is in a shadow or not, and factor that in to our illumination calculation. To sample the shadow map cast by a light, we'll need to transfer texture coordinates from the vertex shader to the fragment shader.
We include Autolight.cginc, a file that contains several macros we will use to sample shadows. SHADOW_COORDS(2) generates a 4-dimensional value with varying precision (depending on the target platform) and assigns it to the TEXCOORD semantic at the provided index (in our case, 2).
Before we can sample the shadow map, however, we need to ensure our shader is set up to handle two different lighting cases: when the main directional light does and does not cast shadows. Unity will help us handle these two configurations by compiled multiple variants of this shader for each use case. You can read more about shader variants here. We will use a built-in shortcut to compile our variants. Add the following line of code just below the #pragma fragment frag line.
SHADOW_ATTENUATION is a macro that returns a value between 0 and 1, where 0 indicates no shadow and 1 is fully shadowed. We multiply NdotL by this value, as it is the variable that stores how much light we received from the main directional light.
Toon shaders come in a wide variety of graphical styles, but achieving the effect usually centers around taking a standard lighting setup (as we did with Blinn-Phong) and applying a step function to it. In fact, when normals and lighting data is available it can be done as a post process effect. An example of this can be found in this tutorial for Unreal Engine 4.
You can contact me about this article at Copied to clipboardr...@gmail.com ?roysta...@gmail.com ?. Sometimes I receive a lot of messages, but I'll try and get back to you as soon as I can!
I made a character and added toon shading but I can't see it. After some research it seems that toon shading is only visible if you render the image with a camera in the blender scene. I want to import the cel shaded model to Unity so I have to need for a camera or light source since I will have to use Unity's. Is it possible to export a "cel shaded" character?
Since a shader is calculated real-time, and not something that can be feasibly stored and exported, this is not possible. Unless unity has a feature to interpret blender's settings and simulate blender's toon shading, which I don't think it does.
I cannot say for certain if it can or can't be done, but I would advise against it regardless. Unity comes with a few toon shaders that are fine for most cases and are guaranteed to work with the engine.
If you want to use blender's toon shader to define a static appearance then you can bake a texture in blender to an image and use an image texture in unity. While baking is most commonly known for creating normal or bump maps you can also bake out entire textures as well.
I'm new to Unity and I've been frustrated for the last 5 days. I'm trying to apply a toon / cel shader to my object which I've imported from Blender. It already has a default material called Player Material which stores the texture of the mesh as imported from Blender.
In this tutorial, you will learn how to write a lit toon shader. You will write a custom shader for Unity URP using HLSL. The shader will receive lighting from a single direction light, and it will cast and receive shadows. This tutorial is based on Unity 2021.3 LTS.
This shading style is characterized by having a limited number of discrete shades of color to create a flat look with blocks of color. Toon shading typically features some specular highlights as well as rim lighting.
c80f0f1006