Ctional light per pixel)
In this section, we will change the previous shader code to the direction light calculated by pixel. We need to split jobs by two shader to determine which operations need to be performed by pixel.
First, let's look at the information received by each vertex:
• Normal
• Semi-Vector
• Light Source Direction
We need to transform the normal to the viewpoint space and then normalize it. We also need to normalize the half vector and the light source direction, but they are already in the viewpoint space. The normalized vectors are interpolated and sent to the segment shader. Therefore, you need to declare variable variables to save these vectors.
We can also complete some calculations related to light and material in vertex shader, which can help balance the load of vertex shader and segment shader.
The vertex shader code can be written as follows:
varying vec4 diffuse,ambient;varying vec3 normal,lightDir,halfVector;void main(){ /* first transform the normal into eye space and normalize the result */ normal = normalize(gl_NormalMatrix * gl_Normal); /* now normalize the light's direction. Note that according to the OpenGL specification, the light is stored in eye space. Also since we're talking about a directional light, the position field is actually direction */ lightDir = normalize(vec3(gl_LightSource[0].position)); /* Normalize the halfVector to pass it to the fragment shader */ halfVector = normalize(gl_LightSource[0].halfVector.xyz); /* Compute the diffuse, ambient and globalAmbient terms */ diffuse = gl_FrontMaterial.diffuse * gl_LightSource[0].diffuse; ambient = gl_FrontMaterial.ambient * gl_LightSource[0].ambient; ambient += gl_FrontMaterial.ambient * gl_LightModel.ambient; gl_Position = ftransform();}
Next, in the segment shader, declare the same variable. In addition, the normal is normalized again, and the light vector does not need to be normalized, because the direction light is consistent with all vertices, And the interpolation result will naturally not change. After that, the point product of the interpolated normal vector and the ray vector is calculated.
varying vec4 diffuse,ambient;varying vec3 normal,lightDir,halfVector;void main(){ vec3 n,halfV; float NdotL,NdotHV; /* The ambient term will always be present */ vec4 color = ambient; /* a fragment shader can't write a varying variable, hence we need a new variable to store the normalized interpolated normal */ n = normalize(normal); /* compute the dot product between normal and ldir */ NdotL = max(dot(n,lightDir),0.0); ...
If the dot product result ndotl is greater than 0, we must calculate the scattered light, that is, multiply the scatter item passed by the vertex shader by this dot product. We also need to calculate the reflected light in the mirror. During the calculation, we first normalize the received half vector, and then calculate the dot product between the half vector and the normal.
... if (NdotL > 0.0) { color += diffuse * NdotL; halfV = normalize(halfVector); NdotHV = max(dot(n,halfV),0.0); color += gl_FrontMaterial.specular * gl_LightSource[0].specular * pow(NdotHV, gl_FrontMaterial.shininess); } gl_FragColor = color;}
The differences between pixel-by-pixel illumination and vertex-by-vertex illumination are displayed:
This section describes the shader designer project:
Http://www.lighthouse3d.com/wp-content/uploads/2011/03/dirpixsd.zip
Point Light per pixel)
This section is based on the previous content about direction light. Most of the Code is the same. This section describes the differences between the direction light and the point light. The direction light is generally assumed that the light source is infinitely far away, so it is a parallel light when it reaches the object. On the contrary, the point light source has a space position and emits light in all directions. In addition, the intensity of the Point Light will weaken as the distance reaches the vertex.
For OpenGL programs, the main differences between the two types of light are:
• W component of the position Field of the light source: the position of the other side is 0 for the light source, and the position of the surface is actually a direction. For the light source, the position is 1.
• The attenuation of a point light source is determined by three coefficients: a constant term, a linear term, and a quadratic term.
For light from the other side, the direction of light is the same for all vertices, but for light, the direction is the vector pointing from the vertex to the position of the light source. So what we need to modify is to add the computing Ray Direction content to the vertex shader.
In OpenGL, attenuation is calculated according to the following formula:
In formula, K0 is the constant attenuation coefficient, K1 is the linear attenuation coefficient, K2 is the secondary attenuation coefficient, and D is the distance from the position of the light source to the vertex.
Note that the attenuation and distance are non-linear relationships, so we cannot calculate the attenuation by vertex and then use the interpolation result in the segment shader. However, we can calculate the distance in the vertex shader, then, use the distance interpolation in the segment shader to calculate the attenuation.
The formula for calculating the color value using the dot light is as follows:
In the above formula, the ambient light must be divided into two parts: the Global ambient light setting using the illumination model and the ambient light setting in the light source. Vertex shader must also calculate the optical components of the two environments respectively. The new vertex shader is as follows:
varying vec4 diffuse,ambientGlobal,ambient;varying vec3 normal,lightDir,halfVector;varying float dist;void main(){ vec4 ecPos; vec3 aux; normal = normalize(gl_NormalMatrix * gl_Normal); /* these are the new lines of code to compute the light's direction */ ecPos = gl_ModelViewMatrix * gl_Vertex; aux = vec3(gl_LightSource[0].position-ecPos); lightDir = normalize(aux); dist = length(aux); halfVector = normalize(gl_LightSource[0].halfVector.xyz); /* Compute the diffuse, ambient and globalAmbient terms */ diffuse = gl_FrontMaterial.diffuse * gl_LightSource[0].diffuse; /* The ambient terms have been separated since one of them */ /* suffers attenuation */ ambient = gl_FrontMaterial.ambient * gl_LightSource[0].ambient; ambientGlobal = gl_FrontMaterial.ambient * gl_LightModel.ambient; gl_Position = ftransform();}
The attenuation needs to be calculated in the segment shader, And the Ray Direction vector obtained by interpolation needs to be normalized, because the light direction to each vertex is generally different.
varying vec4 diffuse,ambientGlobal, ambient;varying vec3 normal,lightDir,halfVector;varying float dist;void main(){ vec3 n,halfV,viewV,ldir; float NdotL,NdotHV; vec4 color = ambientGlobal; float att; /* a fragment shader can't write a varying variable, hence we need a new variable to store the normalized interpolated normal */ n = normalize(normal); /* compute the dot product between normal and normalized lightdir */ NdotL = max(dot(n,normalize(lightDir)),0.0); if (NdotL > 0.0) { att = 1.0 / (gl_LightSource[0].constantAttenuation + gl_LightSource[0].linearAttenuation * dist + gl_LightSource[0].quadraticAttenuation * dist * dist); color += att * (diffuse * NdotL + ambient); halfV = normalize(halfVector); NdotHV = max(dot(n,halfV),0.0); color += att * gl_FrontMaterial.specular * gl_LightSource[0].specular * pow(NdotHV,gl_FrontMaterial.shininess); } gl_FragColor = color;}
Displays the vertex-by-vertex effect of the fixed function and the point-to-light effect calculated by pixel in this section:
This section describes the shader designer project:
Http://www.lighthouse3d.com/wp-content/uploads/2011/03/pointlightsd.zip
Spot Light per pixel)
The content of this section is basically the same as that in the previous section. The only difference is that the concentration is different from the point light, and the light emitted by it is limited to a cone.
For OpenGL programs, the main differences between the two types of light are:
• Concentration contains an direction vector spotdirection, indicating the axis of the cone.
• The cone contains an angle. In glsl, you can use the angle value set by the application and the corresponding cosine value spotcoscutoff.
• There is also an attenuation rate spotexponent, which represents the attenuation of time intensity variation from the center axial outer surface of the cone.
The shader of the concentrating vertex is exactly the same as the vertex light. We only need to make some modifications to the segment shader. Only when the current part is located in the light cone of the concentration, the light components of the scattered light, mirror reflected light, and ambient light must be colored. Therefore, we must first check this condition.
The cosine of the angle between the light source, the link vector of a certain point, and the direction vector of the concentration must be greater than spotcoscutoff. Otherwise, the light source is located outside the concentration and can only receive global ambient light.
...n = normalize(normal);/* compute the dot product between normal and ldir */NdotL = max(dot(n,normalize(lightDir)),0.0);if (NdotL > 0.0){ spotEffect = dot(normalize(gl_LightSource[0].spotDirection), normalize(-lightDir)); if (spotEffect > gl_LightSource[0].spotCosCutoff) { /* compute the illumination in here */ }}gl_FragColor = ...
The following lighting computation is very similar to the spot light. The only difference is that the attenuation must be multiplied by the spotlight effect. The value is calculated according to the following formula:
In the above formula, spotdirection comes from the state set in OpenGL. lightdir is the vector from the light source to a certain point, and spotexp is the concentration attenuation rate. This value is also set in OpenGL, it is used to control the attenuation from the center of the light cone to the edge. The larger the spotexp, the faster the attenuation. If it is 0, the light intensity in the cone is a constant.
spotEffect = pow(spotEffect, gl_LightSource[0].spotExponent);att = spotEffect / (gl_LightSource[0].constantAttenuation + gl_LightSource[0].linearAttenuation * dist + gl_LightSource[0].quadraticAttenuation * dist * dist);color += att * (diffuse * NdotL + ambient);halfV = normalize(halfVector);NdotHV = max(dot(n,halfV),0.0);color += att * gl_FrontMaterial.specular * gl_LightSource[0].specular * pow(NdotHV,gl_FrontMaterial.shininess);
The vertex-by-vertex illumination calculation using the fixed function pipeline and the concentrating effect calculated by Pixel illumination using the shader in this section are displayed respectively.
This section describes the shader designer project:
Http://www.lighthouse3d.com/wp-content/uploads/2011/03/spotlightsd.zip