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] [Next: Toon Shader - Version II]

GLSL Tutorial


Toon Shading - Version I


The first version presented in here computes an intensity per vertex. Then the fragment shader uses the vertex interpolated intensity to compute a tone for the fragment. The vertex shader must therefore declare a varying variable to store the intensity. The fragment shader must declare the same variable, also using the varying qualifier, to receive the properly interpolated value for the intensity.

The light direction could be defined in the vertex shader as a local variable or as a constant, however having it as a uniform variable provides more freedom since it can be set arbitrarily on the OpenGL application. The light's direction variable will be defined in the shader as

	uniform vec3 lightDir;

For now, lets assume that the light's direction is defined in world space.

The vertex shader has access to the normals, as specified in the OpenGL application, through the attribute variable gl_Normal. This is the normal as defined in the OpenGL application with the glNormal function, hence in model local space.

If no rotations or scales are performed on the model in the OpenGL application, then the normal defined in world space, provided to the vertex shader as gl_Normal, coincides with the normal defined in the local space. The normal is a direction and therefore it is not affected by translations.

Because both the normal and the light's direction are specified in the same space, the vertex shader can jump directly to the cosine computation between the light's direction, i.e. lightDir, and the normal. The cosine can be computed using the following formula

	cos(lightDir,normal) = lightDir . normal / ( |lightDir| * |normal| )
where "." is the inner product, aka as the dot product. This can be simplified if both the normal and lightDir are normalized, i.e.
	| normal | = 1
	| lightDir | = 1
Hence if these two conditions are guaranteed the computation for the cosine can be simplified to
	cos(lightDir,normal) = lightDir . normal 
Since the variable lightDir is supplied by the OpenGL application we can assume that it arrives at the shader already normalized. It would be a waste of time having to normalize it for every vertex, instead of performing it only when the lightDir changes. Also it is reasonable to expect that the normals from the OpenGL application are normalized.

Therefore the cosine, which we will store in a variable named intensity, can be computed with the dot function provided by GLSL.

	intensity = dot(lightDir, gl_Normal);

The only thing that's left to do in the vertex shader is to transform the vertex coordinates. The complete code for the shader is as follows:


	uniform vec3 lightDir;
	
	varying float intensity;
	
	void main()
	{
		vec3 ld;
		
		intensity = dot(lightDir,gl_Normal);
		
		gl_Position = ftransform();
	} 

Now, in the fragment shader, all that's left to do is to define a color for the fragment based on the intensity. The intensity must be passed on to the fragment shader, since it is the fragment shader that is responsible for setting the colors for fragments. As mentioned before, the intensity will be defined as a varying variable on both shaders, hence it must be written in the vertex shader for the fragment shader to read it.

The color can be computed in the fragment shader as follows:


	vec4 color;
	
	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);


As can be seen from the code above, the brightest color is used when the cosine is larger than 0.95 and the darker color is used for cosines smaller than 0.25. All there is left to do in the fragment shader is to set the gl_FragColor based on the color. The code for the fragment shader is:


	varying float intensity;
	
	void main()
	{
		vec4 color;
		
		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 following image shows the end result, and it doesn't look very nice does it? The main problem is that we're interpolating the intensity. This is not the same as computing the intensity with the proper normal for the fragment. Go on to the next section to see toon shading done properly!

[Previous: Toon Shader] [Next: Toon Shader - Version II]

       


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

Lighthouse 3D privacy statement