OpenGL VRML W3D            
  Home Tutorials Books Applications Tools Docs Models Textures  

 

              Bugs

GLSL Tutorial   

  GLSL Tutorial

Index
Introduction

The Graphics Pipeline
Pipeline Overview
Vertex Processor
Fragment Processor

OpenGL Setup for GLSL
Overview
Creating a Shader
Creating a Program
Source Code
Trouble Shooting: the InfoLog
Cleaning Up

Comm. OpenGL=> GLSL
Comm. Introduction
Uniform Variables
Attribute Variables

Shader Basics
Data Types and Variables
Statments and Functions
Varying Variables

Shader Examples
Shader Examples List

GLSL Hello World

Color Shader

Flatten Shader

Toon Shader
Toon Shader - Version I
Toon Shader - Version II
Toon Shader - Version III

Lighting
OpenGL Directional Light I
OpenGL Directional Light II
Directional Light per Pixel
Point Light Per Pixel
Spot Light Per Pixel

Simple Texture
Combine Texture + Fragment
Multitexturing

Notes
The gl_NormalMatrix
Normalization Issues


Google

OpenGLTutorials @ Lighthouse3d.com

Led Shader
View Frustum Culling
GLSL Tutorial
Maths Tutorial
Billboarding Tutorial
Picking Tutorial
Terrain Tutorial
Display Lists Tutorial
GLUT Tutorial



   
[Previous: Toon Shader - Version I] [Next: Toon Shader - Version III]

GLSL Tutorial


Toon Shader - Version II


GLSL has access to part of the OpenGL state. In this tutorial we'll see how to access the color as set in an OpenGL application with glColor.

GLSL has an attribute variable where it keeps track of the current color. In this section we will do the toon shader effect per fragment. In order to do that, we need to have access to the fragments normal per fragment. Hence the vertex shader only needs to write the normal into a varying variable, so that the fragment shader has access to the interpolated normal.

The vertex shader gets simplified, since the color intensity computation will now be done in the fragment shader. The uniform variable lightDir also has moved to the fragment shader, since it is no longer used in the vertex shader. See the code bellow for the new vertex shader:


	varying vec3 normal;
	
	void main()
	{
		normal = gl_Normal;
	
		gl_Position = ftransform();
	} 

In the fragment shader we now need to declare the uniform variable lightDir since the intensity is based on this variable. A varying variable is also defined to receive the interpolated normal. The code for the fragment shader then becomes:


	uniform vec3 lightDir;
	
	varying vec3 normal;
	
	void main()
	{
	
		float intensity;
		vec4 color;
		
		intensity = dot(lightDir,normal);
		
		if (intensity > 0.95)
			color = vec4(1.0,0.5,0.5,1.0);
		else if (intensity > 0.5)
			color = vec4(0.6,0.3,0.3,1.0);
		else if (intensity > 0.25)
			color = vec4(0.4,0.2,0.2,1.0);
		else
			color = vec4(0.2,0.1,0.1,1.0);
		
		gl_FragColor = color;
	} 

And the result is:

No, its not a bug! Its the same result as in the previous section. So what happened?

Let's look closely at the differences between the two versions. In the first version we computed an intensity in the vertex shader and used the interpolated value in the fragment shader. In the second version we interpolated the normal, in the vertex shader, for the fragment shader where we computed the dot product. Interpolation and dot product are both linear operations, so it doesn't matter if we compute the dot product first and then interpolate, or if we interpolate first and then compute the dot product.

What is wrong in here is the usage of the interpolated normal for the dot product in the fragment shader! And it is wrong because the normal, although it has the right direction, it most likely has a not unit length.

We know that the direction is right because we assumed that the normals that arrived at the vertex shader were normalized, and interpolating normalized vectors, provides a vector with the correct direction. However the length is wrong in the general case because interpolating normalized normals only yields a unit length vector if the normals being interpolated have the same direction, which is highly unlikely in smooth surfaces. See Normalization Issues for more details.

The main reason to move the intensity computation from the vertex shader to the fragment shader was to compute it using the proper normal for the fragment. We have a normal vector that has the correct direction but is not unit length. In order to fix this all we have to do is to normalize the incoming normal vector at the fragment shader. The following code is the correct and complete toon shader:


	uniform vec3 lightDir;
	
	varying vec3 normal;
	
	void main()
	{
	
		float intensity;
		vec4 color;

		
		intensity = dot(lightDir,normalize(normal));
		
		if (intensity > 0.95)
			color = vec4(1.0,0.5,0.5,1.0);
		else if (intensity > 0.5)
			color = vec4(0.6,0.3,0.3,1.0);
		else if (intensity > 0.25)
			color = vec4(0.4,0.2,0.2,1.0);
		else
			color = vec4(0.2,0.1,0.1,1.0);
		
		gl_FragColor = color;
	} 

The result for this version of the toon shader is depicted below. It looks nicer, yet it is not perfect. It suffers from aliasing, but this is outside the scope of this tutorial ;)

In the next section we will use an OpenGL light to set the light's direction of the shader.

[Previous: Toon Shader - Version I] [Next: Toon Shader - Version III]

       


Site designed and maintained by António Ramires Fernandes
Your comments, suggestions and references to further material are welcome!

Lighthouse 3D privacy statement