Cartoon-style graphics have always had a special place in the world of gaming. The unique look and feel of cel-shaded lighting bring characters and environments to life with a distinct visual charm. In this tutorial, we will explore the art of cel-shaded lighting, taking cues from popular games like Jet Set Radio and The Legend of Zelda: The Wind Waker.

Modifying the Surface Shader

To create our cel-shaded effect, we will build upon the diffuse shaders from the previous tutorial. However, we need to make some modifications to the surface shader variant. Instead of relying on Unity's built-in lighting model, we will write our own custom lighting model to manipulate lighting directly.

Open the Shaders/CelShadedSurf.shader file. Currently, it is identical to the finished DiffuseSurf shader from the previous tutorial. We will now write our custom lighting model.

Read more: How LED Light Strips Work

Building Our Custom Lighting Model

In our current setup, we are using the standard lighting model, which includes the SurfaceOutputStandard struct. However, for our cel-shaded effect, we will use the more basic SurfaceOutput struct. To define our custom lighting model, we will create a function with a name beginning with "Lighting". Make sure to adhere to the specific semantics required by Unity.

Since we named our custom lighting model "Cel" and not the standard model, we need to update the #pragma directive that tells Unity which lighting model to use.

Additionally, we will tweak the surf function to take a SurfaceOutput instead of a SurfaceOutputStandard.

With these changes in place, we can now define the behavior of our new cel lighting model. The calculations will be similar to what we did in the fragment shader of the previous tutorial, using the dot product to determine the angle between the normal vector and the directional light's direction.

Read more: How to Hide LED Strip Lights in Your Home

Implementing Cel-Shading

The most basic way to implement cel-shading is to specify a cutoff point on the dot product value. Values above the cutoff are considered lit, while values below the cutoff are shaded. This approach gives us a two-tone cel-shaded effect.

However, we can go beyond this basic implementation to improve the rendering. For example, we can add specular lighting, create more flexible cutoff points for additional lighting "bands," and introduce a smooth falloff to prevent aliasing artifacts.

Smooth Falloff

To achieve a smooth falloff effect, we will use Unity's fwidth function, which calculates how fast a value changes between the current pixel and adjacent pixels. We will combine this with the smoothstep function, which returns a value between 0 and 1 based on where a parameter lies between upper and lower bounds.

By calculating a delta value using fwidth and multiplying it with a property called _Antialiasing, we can control the amount of falloff. This creates a smooth transition from fully lit to fully shaded sections of the object.

Read more: 

Digital RGB Flexible LED Strip Lights Wall Washer - A 100W Flexible Linear LED Lighting Solution

Specular Lighting

In cel shading, diffuse lighting does not take the view direction into account. However, specular lighting does. Specular highlights are the shiny parts of an object that appear when light reflects off a surface and into the viewer's eyes.

To implement specular lighting, we need to pass the view direction into the lighting model function. We also need to specify the shininess of the material and the color of the specular highlights. We can use the directional light's color in a similar way to our diffuse component.

By calculating the half vector and performing the dot product between the normal vector and the half vector, we can determine the intensity of the specular highlights. Multiplying this with the existing smooth diffuse value allows us to control the specularity using a value in the Inspector.

To create a smooth transition for the specular highlights, we will use the smoothstep function and define our own upper bound controlled by the _Antialiasing property.

Finally, we will add the diffuse and specular light values together in the final lighting calculation.

Vertex and Fragment Shader Variants

For completeness, we will also provide a vertex and fragment shader variant. Most of the code will be identical or very similar to the surface shader version. However, we need to calculate the view direction ourselves using Unity's WorldSpaceViewDir() function and include the Lighting.cginc file.

With these changes, you should now see a cel-shaded object similar to the surface shader version.

In this tutorial, we covered the basics of cel shading and learned how to implement a simple two-tone cel shader. We then enhanced it by introducing a smooth falloff and adding specular lighting. In the next tutorial, we will explore more advanced techniques such as normal/bump mapping and fresnel lighting.

Frequently Asked Questions

Q: Which popular games have used cel-shaded lighting?
A: Some popular games that have utilized cel-shaded lighting include Jet Set Radio, The Legend of Zelda: The Wind Waker, and the Borderlands franchise.

Q: What is the purpose of using thresholds in cel-shaded lighting?
A: Thresholds in cel-shaded lighting help create solid shading bands, giving objects a non-realistic, cartoonish look.

Q: Can I customize the cutoff points for cel-shading?
A: Yes, by implementing additional cutoff points, you can create more lighting "bands" and customize the cel-shading effect further.

Q: How can I prevent sharp transitions in cel-shaded lighting?
A: Applying smooth falloff using the smoothstep function can help prevent sharp transitions and aliasing artifacts, resulting in a more visually pleasing cel-shaded effect.

Q: What is the role of specular lighting in cel shading?
A: Specular lighting adds shiny highlights to objects and takes the view direction into account. By controlling the specularity and color of the highlights, you can enhance the overall appearance of your cel-shaded graphics.