# Toon Shading – Version III

Prev: Toon - Version II | Next: Lighting |

Before we finish this tutorial there is just one more thing: we’re going to use an OpenGL light instead of the variable lightDir. In this way we can define a light in the OpenGL application and use that light’s direction in our shader. Note: it is not necessary to turn on the lights using *glEnable*, since we are using shaders.

We shall assume that the first light (GL_LIGHT0) in the OpenGL application is a directional light.

GLSL provides access to part of the OpenGL state, namely the lights properties. GLSL declares a C type struct for the lights properties, and an array to store these properties for each of the lights.

struct gl_LightSourceParameters { vec4 ambient; vec4 diffuse; vec4 specular; vec4 position; ... }; uniform gl_LightSourceParameters gl_LightSource[gl_MaxLights];

This means that we can access the light’s direction (using the *position* field of a directional light) in the vertex shader. Again we shall assume that the light’s direction is normalized by the OpenGL application.

The OpenGL specification states that when a light position is set it is automatically converted to eye space coordinates, i.e. camera coordinates. We can assume that the light position stays normalized when automatically converted to eye space. This will be true if we the upper left 3×3 sub matrix of the modelview matrix is orthogonal (this is ensured if we set the camera using gluLookAt, and we don’t use scales in our application).

We have to convert the normal to eye space coordinates as well to compute the dot product, as it only makes sense to compute angles, or cosines in this case, between vectors in the same space, and as mentioned before the light position is stored in eye coordinates.

To transform the normal to eye space we will use the pre-defined uniform variable mat3 *gl_NormalMatrix*. This matrix is the transpose of the inverse of the 3×3 upper left sub matrix from the modelview matrix. We will do the normal transformation per vertex. The vertex shader then becomes:

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

In the fragment shader we must access the light position to compute the intensity:

varying vec3 normal; void main() { float intensity; vec4 color; vec3 n = normalize(normal); intensity = dot(vec3(gl_LightSource[0].position),n); 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; }

A Shader Designer project is available in here. Source code based on GLUT and GLEW is available in here: ARB extensions syntax or OpenGL 2.0 syntax.

Prev: Toon - Version II | Next: Lighting |

the light is error. u can’t use light pos as a direction.

the light dir in VS should be :

vec4 p0 = gl_ModelViewMatrix * gl_Vertex;

gl_Position = gl_ProjectionMatrix * p0;

lightDir = normalize(vec3(gl_LightSource[0].position-p0));

normal = gl_NormalMatrix * gl_Normal;

The light is correct, he is using a directional light, i.e it is constant for all vertices. In this case the direction of he light has exactly the same values as its position.

Your assumption that the light position is normalized seems wrong (or else i am missing something). That upper 3×3 matrix could be orthogonal and still have scaling in it. That submatrix needs to be orthonormal (i.e., containing only rotation, and not scale or skew).

An orthogonal matrix, aka orthonormal matrix, has its vectors (columns or rows) as unit vectors, as well as perpendicular to each other. Hence if a vector is normalized prior to being transformed with such a matrix, it will still be a unit length vector after being transformed.

Dave is right.

Orthogonal and Orthonormal are different (rather almost similar) concepts.

– Orthogonal: all vectors are perpendicular;

– Orthonormal: an orthogonal matrix which have only unit vectors.

The determinant of an orthogonal matrix may be different or equal to 1, but det(orthonormal_matrix) is always equal to 1.