Eighth Task
Show diferent lighting options in a scene
All code can be found on github, but the live version is also available on Codepen. You may see a live demo at my webpage.
This project was done with help from task 5: Camera Changes and WebGLFundamentals.
Lighting is simple to explain, but hard to understand: it refers to the amount of color that bounces off from an element, when hit by photons. In the WebGL context, this means the amount of color that an object has.
Up until now, everything had had Ambient lighting, which meant that everything was evenly illuminated from all directions, and all surfaces had full color saturation.
Now, Directional lighting refers to the light that comes from a single source in a parallel manner. Think of the sun, the difference in degrees of each photon is minute, and so small we can disregard it.
Next, Point lighting is a light source in point, that generates light in all directions.
Lastly, Spot lighting is similar to spot ligting, but it does not do rays, but rather cones of light.

Now, WebGL has no API to do this, we have to modify the Shaders to get these effects to work, and we need to calculate the surface vector.
The thing is, that each lighting will require different shaders, and the button click will switch those shaders (and maybe change a function a little).
Directional
The vertex shader will be:
<script id="3d-vertex-shader" type="x-shader/x-vertex">
attribute vec4 a_position;
attribute vec3 a_normal;
uniform mat4 u_matrix;
//The surface normal
varying vec3 v_normal;
void main() {
// Multiply the position by the matrix.
gl_Position = u_matrix * a_position;
// Pass the color to the fragment shader.
v_normal = a_normal;
}
</script>
And the fragment shader will be in charge of normalizing, and calculating the dot product of the light source and the surface, and finally, finding the color by multiplying by the amount of light received.
<script id="3d-fragment-shader" type="x-shader/x-fragment">
precision mediump float;
// Passed in from the vertex shader.
varying vec3 v_normal;
uniform vec3 u_reverseLightDirection; //(light reflected from the surface)
uniform vec4 u_color;
void main() {
// because v_normal is a varying it's interpolated
// we it will not be a uint vector. Normalizing it
// will make it a unit vector again
vec3 normal = normalize(v_normal);
// take the dot product
float light = dot(normal, u_reverseLightDirection);
gl_FragColor = u_color;
// Lets multiply just the color portion (not the alpha)
// by the light
gl_FragColor.rgb *= light;
}
</script>
Now, finding the normals is not trivial, but is easy. To do this in a given triangle such that it's vertices are $$P_1=[x_1, y_1, z_1],P_2=[x_2, y_2, z_2],P_3=[x_3, y_3, z_3]$$, then we take the cross product oftwo of the sides, and get the normal vector.
$$n=(P_2 - P_1) × (P_3 - P_1)$$
![]()
We do this every draw to calculate the current normal, because objects are moving. Mind you, it may seem hard, but remember that in the GPU, matrix operations are really fast.
We will use this normal for all the different lighting options.
Spot
Spot lighting uses a cone of light, just like a real spotlight.

This one has different problems, specially since it does not hit the whole area, but the same concepts apply.