Getting Cel Shaded to Work in Your Pipeline

The thing about cel shaded rendering is that most people approach it backwards. They try to make it look flat at the shader level and end up with something that looks unfinished rather than stylized. I spent about three weeks last year debugging why my cartoon characters kept looking like they had mud on them instead of proper hard edges between color bands. Cel shaded is a rendering technique that reduces continuous gradients into discrete color bands. The name comes from traditional animation where artists painted on clear celluloid sheets, hence "cel." In 3D graphics, you're simulating that same look by quantizing the lighting values. A typical implementation uses a threshold-based approach where the shader checks whether the dot product between the normal and light direction falls above or below certain ranges, then outputs the corresponding color band. The standard Unity shader for this looks something like:

float diff = dot(normal, lightDir) * 0.5 + 0.5;
diff = ceil(diff * 4.0) / 4.0;
return diff; That's basically it for the core mechanic. You take a value from 0 to 1, multiply by however many bands you want, round up, then divide back down. Four bands gives you three shadow transitions plus the lit area.

The Edge Case That Broke My Project

Here's the problem nobody warns you about: when your normals get interpolated across a low-poly mesh, the edges between color bands become jagged or disappear entirely on curved surfaces. I ran into this with a character model that had only eight polygons per face. The hard edges I wanted for the anime look were turning into aliased messes whenever the camera angle changed. My workaround was to bake vertex normals into the tangent space and use a custom normal map that I painted specifically for the shader. Instead of relying on Smooth shading, I created a discrete normal atlas where each region of the model had normals pointing exactly where I wanted the band transitions to happen. It took about two days to paint, but it eliminated the banding artifacts completely. The trick is treating the normal map like a stencil for your shadow edges rather than a surface detail layer. Another approach that works if you can't bake normals is using a depth-based screen space trick. Render the scene to a depth texture, blur it slightly, then compare the original depth against the blurred version. Areas where they diverge significantly are edges, and you can harden the lighting quantization there. This adds roughly 15 milliseconds to your render time on a mid-range GPU, which is acceptable for mobile if you're targeting 30fps.

Get the Full Details

Cel Shading Art Style
Cel Shading Art Style

Common Mistakes That Waste Time

People usually start by tweaking the lighting thresholds in the shader. That's the wrong place to begin. The visual result depends more on your vertex count and how normals are smoothed than on the shader math itself. I've seen artists spend four hours adjusting shader parameters when the actual fix was just increasing the subdivision on their character meshes or enabling auto-normal smoothing in Blender. If you're working in Unreal Engine, the built-in Cel Shader node in Material Editor has a Bands input that defaults to three. Setting it to five or six gives you finer control, but the artifact problems I described earlier still apply. The engine's forward rendering path handles cel shading better than deferred, so check your renderer settings if your shadows look wrong. For real-time applications on mobile, you might need to reduce the band count to three to keep your draw calls efficient. The human eye actually perceives three bands as sufficient for a stylized look, and anything beyond five starts looking like a tech demo rather than intentional art direction. I learned this the hard way when a producer complained that our six-band character models looked "over-rendered" during a playtest.

When Cel Shaded Fails Completely

There are scenarios where this technique simply doesn't work. If you need subsurface scattering for translucent materials like ears or leaves, cel shading makes them look like plastic. The discrete bands don't approximate the way light actually penetrates skin, and trying to fake it with additional masks usually creates more problems than it solves. In those cases, I recommend falling back to a hybrid approach where you apply cel shading only to the opaque geometry and use standard PBR for the translucent parts. Dynamic lighting with multiple light sources also gets messy. Each light needs its own banding calculation, and the artifacts compound quickly. A single directional light is fine. Two or more, and you'll spend hours adjusting because the shadow edges start fighting each other. Our team switched to pre-baked lighting for outdoor scenes in our last project because real-time multi-light cel shading was eating too much of our frame budget without looking any better. The other limitation is that cel shading doesn't age well on low-resolution displays. On a 720p phone screen, your four color bands might actually render as three or even two due to pixel averaging. Test on the target hardware early, preferably with an actual device rather than an emulator, because what looks crisp on your 4K monitor turns into muddy blobs on the thing your players will actually use.

If you need this style for a static game or cutscene, consider pre-rendering to texture instead. It gives you pixel-perfect control and eliminates all the performance concerns. The trade-off is obviously no interactivity, but if your project doesn't require real-time rendering, that's often the cleanest solution.

Cel Shading
Cel Shading