Time for action — updating uniforms in real time

  1. Open the file ch3_Sphere_Goraud_Lambert.html in your favorite HTML5 browser.
  2. You will see that this example has some widgets at the bottom of the page. These widgets were created using JQuery UI. You can check the code for those in the HTML<body> of the page.
    • X,Y,Z: controls the direction of the light. By changing these sliders you will modify the uniform uLightDirection.
    • Sphere color: changes the uniform uMaterialDiffuse, which represents the diffuse color of the sphere. Here we use a color selection widget so you can try different colors. The updateObjectColor function receives the updates from the widgets and updates the uMaterialDiffuse uniform.
    • Light diffuse term: changes the uniform uLightDiffuse, which represents the diffuse color of the light source. There are no reasons as to why the light color has to be white; however for the sake of simplicity, in this case, we are using a slider instead of a color to restrict the light color to the gray scale. We achieve this by assigning the slider value to the RGB components of uLightDiffuse while we keep the alpha channel set to 1.0. We do this inside the updateLightDiffuseTerm function, which receives the slider updates.
  3. Try different settings for light source position (which will affect the light-direction vector), the diffuse material, and light properties.
Time for action — updating uniforms in real time

What just happened?

We have seen an example of a simple scene illuminated using Goraud interpolation and a Lambertian reflection model. We have also seen the immediate effects of changing uniform values for the Lambertian lighting model.

Have a go hero — moving light

We have mentioned before that we use matrices to move the camera around the scene. Well, we can also use matrices to move lights!

  1. Check the file ch3_Sphere_Moving.html using your favorite source code editor. The vertex shader is very similar to the previous diffuse model example. However, there is one extra line:
    vec4 light = uMVMatrix * vec4(uLightDirection, 0.0);
    

    Here we are transforming the uLightDirection vector to the light variable. Notice that the uniform uLightDirection is a vector with three components (vec3) and that uMVMatrix is a 4x4 matrix. In order to do the multiplication, we need to transform this uniform to a four-component vector (vec4). We achieve this with the construct:

    vec4(uLightDirection, 0.0);
    

    The matrix uMVMatrix contains the Model-view-transform. We will see how all this works in the next chapter. However, for now, let's say that this matrix allows us to update vertices positions and also, as we see in this example, lights positions.

  2. Take another look at the vertex shader. In this example, we are rotating the sphere and the light. Every time the drawScene function is invoked, we rotate the matrix mvMatrix a little bit in the y axis:
    mat4.rotate(mvMatrix, angle * Math.PI / 180, [0, 1, 0]);
    
  3. If you examine the code more closely, you will notice that the matrix mvMatrix is mapped to the uniform:
    uMVMatrix:gl.uniformMatrix4fv(prg.uMVMatrix, false, mvMatrix);
    
  4. Now run the example in your HTML5 browser. You will see a sphere and a light source rotating on the y-axis:
    Have a go hero — moving light
  5. Look for the initLights function and change the light orientation so the light is pointing in the negative z-axis direction:
    gl.uniform3f(prg.uLightDirection, 0.0, 0.0, -1.0)
    
  6. Save the file and run it again. What happened? Now change the light direction uniform so it points to [-1.0, 0.0, 0.0]. Save the file and run it again on your browser. What happened?
  7. Now set the light back to the 45 degree angle by changing the uniform uLightDirection so it goes back to its initial value:
    gl.uniform3f(prg.uLightDirection, 0.0, 0.0, -1.0)
    
  8. Go to drawScene and change the line:
    mat4.rotate(mvMatrix, angle * Math.PI / 180, [0, 1, 0]);
    

    with:

    mat4.rotate(mvMatrix, angle * Math.PI / 180, [1, 0, 0]);
    
  9. Save the file and launch it again in your browser. What happens?

What can you conclude? As you see, the vector that is passed as the third argument to mat4.rotate determines the axis of the rotation. The first component corresponds to the x-axis, the second to the y-axis and the third to the z-axis.

Goraud shading with Phong reflections

In contrast with the Lambertian reflection model, the Phong reflection model considers three properties: the ambient, diffuse, and specular. Following the same analogy that we used in the previous section:

Final Vertex Color = Ia + Id + Is

where:

Ia = Light Ambient Property * Material Ambient Property
Id = Light Diffuse Property * Material Diffuse Property * Lambert coefficient
Is = Light Specular Property * Material Specular Property * specular coefficient

Please notice that:

  • As we are using Goraud interpolation, we still use vertex normals to calculate the diffuse term. This will change when using Phong interpolation where we will be using fragment normals.
  • Both light and material have three properties: the ambient, diffuse, and specular colors.
  • We can see on these equations that Ia, Id, and Is receive contributions from their respective light and material properties.

Based on our knowledge of the Phong reflection model, let's see how to calculate the specular coefficient in ESSL:

float specular = pow(max(dot(R, E), 0.0), f );

where:

E is the view vector or camera vector.

R is the reflected light vector.

f is the specular exponential factor or shininess.

R is calculated as:

R = reflect(L, N)

where N is the vertex normal considered and L the light direction that we have been using to calculate the Lambert coefficient.

Let's take a look at the ESSL implementation for the vertex and fragment shaders.

Vertex shader:

attribute vec3 aVertexPosition;
attribute vec3 aVertexNormal;
uniform mat4 uMVMatrix;
uniform mat4 uPMatrix;
uniform mat4 uNMatrix;
uniform float uShininess;
uniform vec3 uLightDirection;
uniform vec4 uLightAmbient;
uniform vec4 uLightDiffuse;
uniform vec4 uLightSpecular;
uniform vec4 uMaterialAmbient;
uniform vec4 uMaterialDiffuse;
uniform vec4 uMaterialSpecular;
varying vec4 vFinalColor;
void main(void) {
vec4 vertex = uMVMatrix * vec4(aVertexPosition, 1.0);
vec3 N = vec3(uNMatrix * vec4(aVertexNormal, 1.0));
vec3 L = normalize(uLightDirection);
float lambertTerm = clamp(dot(N,-L),0.0,1.0);
vec4 Ia = uLightAmbient * uMaterialAmbient;
vec4 Id = vec4(0.0,0.0,0.0,1.0);
vec4 Is = vec4(0.0,0.0,0.0,1.0);
Id = uLightDiffuse* uMaterialDiffuse * lambertTerm;
vec3 eyeVec = -vec3(vertex.xyz);
vec3 E = normalize(eyeVec);
vec3 R = reflect(L, N);
float specular = pow(max(dot(R, E), 0.0), uShininess );
Is = uLightSpecular * uMaterialSpecular * specular;
vFinalColor = Ia + Id + Is;
vFinalColor.a = 1.0;
gl_Position = uPMatrix * vertex;
}

We can obtain negative dot products for the Lambert term when the geometry of our objects is concave or when the object is in the way between the light source and our point of view, in either case the negative of the light-direction vector and the normals will form an obtuse angle producing a negative dot product, as shown in the following figure:

Goraud shading with Phong reflections

For that reason we are using the ESSL built-in clamp function to restrict the dot product to the positive range. In the case of obtaining a negative dot product, the clamp function will set the lambert term to zero and the respective diffuse contribution will be discarded, generating the correct result.

Given that we are still using Goraud interpolation, the fragment shader is exactly as before:

#ifdef GL_ES
precision highp float;
#endif
varying vec4 vFinalColor;
void main(void)
{
gl_FragColor = vFinalColor;
}

In the following section, we will explore the scene and see what it looks like when we have negative Lambert coefficients that have been clamped to the [0,1] range.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.145.47.253