Creating a cartoon shading effect

Toon shading (also called Celshading) is a non-photorealistic technique that is intended to mimic the style of shading often used in hand-drawn animation. There are many different techniques that are used to produce this effect. In this recipe, we'll use a very simple technique that involves a slight modification to the ambient and diffuse shading model.

The basic effect is to have large areas of constant color with sharp transitions between them. This simulates the way that an artist might shade an object using strokes of a pen or brush. The following figure shows an example of a teapot and torus rendered with toon shading.

Creating a cartoon shading effect

The technique presented here involves computing only the ambient and diffuse components of the typical ADS shading model, and quantizing the cosine term of the diffuse component. In other words, the value of the dot product normally used in the diffuse term is restricted to a fixed number of possible values. The following table illustrates the concept for four levels:

Cosine of the Angle between s and n

Value used

Between 1 and 0.75

0.75

Between 0.75 and 0.5

0.5

Between 0.5 and 0.25

0.25

Between 0.25 and 0.0

0.0

In the preceding table, s is the vector towards the light source and n is the normal vector at the surface. By restricting the value of the cosine term in this way, the shading displays strong discontinuities from one level to another (see the preceding figure), simulating the pen strokes of hand-drawn cel animation.

Getting ready

Start with the same vertex shader from the Using per-fragment shading for improved realism recipe. Your OpenGL program must set the values for all uniform variables defined in that vertex shader as well as the fragment shader code described below.

How to do it...

To create a shader program that produces a toon shading effect, use the following fragment shader:

in vec3 Position;
in vec3 Normal;

struct LightInfo {
  vec4 position;
  vec3 intensity;
};
uniform LightInfo Light;

uniform vec3 Kd;            // Diffuse reflectivity
uniform vec3 Ka;            // Ambient reflectivity

const int levels = 3;
const float scaleFactor = 1.0 / levels;
layout( location = 0 ) out vec4 FragColor;
vec3 toonShade( )
{
  vec3 s = normalize( Light.position.xyz - Position.xyz );
  float cosine = max( 0.0, dot( s, Normal ) );
  vec3 diffuse = Kd * floor( cosine * levels ) * 
                 scaleFactor;

  return Light.intensity * (Ka + diffuse);
}

void main() {
  FragColor = vec4(toonShade(), 1.0);
}

How it works...

The constant variable, levels, defines how many distinct values will be used in the diffuse calculation. This could also be defined as a uniform variable to allow for configuration from the main OpenGL application. We will use this variable to quantize the value of the cosine term in the diffuse calculation.

The toonShade function is the most significant part of this shader. We start by computing s, the vector towards the light source. Next, we compute the cosine term of the diffuse component by evaluating the dot product of s and Normal. The next line quantizes that value in the following way. Since the two vectors are normalized, and we have removed negative values with the max function, we are sure that the value of cosine is between zero and one. By multiplying this value by levels and taking the floor, the result will be an integer between 0 and levels –1. When we divide that value by levels (by multiplying by scaleFactor), we scale these integral values to be between zero and one again. The result is a value that can be one of levels possible values spaced between zero and one. This result is then multiplied by Kd, the diffuse reflectivity term.

Finally, we combine the diffuse and ambient components together to get the final color for the fragment.

There's more...

When quantizing the cosine term, we could have used ceil instead of floor. Doing so would have simply shifted each of the possible values up by one level. This would make the levels of shading slightly brighter.

The typical cartoon style seen in most cel animation includes black outlines around the silhouettes and along other edges of a shape. The shading model presented here does not produce those black outlines. There are several techniques for producing them, and we'll look at one later on in this book.

See also

  • The Using per-fragment shading for improved realism recipe
  • The Implementing per-vertex ambient, diffuse, and specular (ADS) shading recipe in Chapter 2, The Basics of GLSL Shaders
  • The Drawing silhouette lines using the geometry shader recipe in Chapter 6, Using Geometry and Tessellation Shaders
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.189.180.244