The first version preseted 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;//一致变量,从opengl程序传过来
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 | = 1Hence if these two conditions are guaranteed the computation for the cosine can be simplified to
cos(lightDir,normal) = lightDir . normalSince 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!
//--------被充
这个例子很简单,就是在程序中设置光照的方向后,如果光照的方向与顶点向量的方向的余弦在某个范围内,设置不同的艳色