Search code examples
glslwebglphong

Using Phong lighting with directional lighting


I wrote a simple demo-program that applies directional lighting to a scene. Right now I'm getting this image:

Current image I get

The problem is that I can't figure out how can I set object's material reflection settings (ambient, diffuse, specular, shininess, emission).

Here's my current code:

Vertex shader:

attribute vec4 a_position;
attribute vec3 a_normal;

uniform mat4 u_worldViewProjectionMatrix;
uniform mat4 u_worldInverseTransposeMatrix;

varying vec3 v_normal;

void main() {
    // Multiply the position by the matrix.
    gl_Position = u_worldViewProjectionMatrix * a_position;

    // Copy the color from the attribute to the varying.
    v_normal = mat3(u_worldInverseTransposeMatrix) * a_normal;
}

Fragment shader:

precision mediump float;

varying vec3 v_normal;

uniform vec3 u_reverseLightDirection;
uniform vec4 u_color;

void main() {
    vec3 normal = normalize(v_normal);

    float light = dot(normal, u_reverseLightDirection);

    gl_FragColor = u_color;

    // Multiplying color portion by the light
    gl_FragColor.rgb *= light;
}

Scene-rendering function:

function draw() {
    gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);

    // Clear the canvas.
    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

    gl.enable(gl.CULL_FACE);
    gl.cullFace(gl.BACK);
    gl.enable(gl.DEPTH_TEST);

    // Tell it to use our program (pair of shaders)
    gl.useProgram(shaderProgram);

    addCubeToScene();
  }

  function addCubeToScene() {
    // Turn on the attribute
    gl.enableVertexAttribArray(positionAttr.loc);

    // Bind the position buffer.
    gl.bindBuffer(gl.ARRAY_BUFFER, shaderProgram.positionBuffer);

    // Put geometry data into buffer
    setCubeGeometry(gl);

    // Tell the attribute how to get data out of positionBuffer (ARRAY_BUFFER)
    var type = gl.FLOAT; // the data is 32bit floats
    var normalize = false; // don't normalize the data
    var stride = 0; // 0 = move forward size * sizeof(type) each iteration to get the next position
    var offset = 0; // start at the beginning of the buffer
    gl.vertexAttribPointer(
      positionAttr.loc,
      positionAttr.size - 1,
      type,
      normalize,
      stride,
      offset
    );

    // Turn on the attribute
    gl.enableVertexAttribArray(normalAttr.loc);

    // Set normals
    gl.bindBuffer(gl.ARRAY_BUFFER, shaderProgram.normalBuffer);

    // Put normals data into buffer
    setNormals(gl);

    // Tell the attribute how to get data out of normalBuffer (ARRAY_BUFFER)
    var type = gl.FLOAT; // the data is 32bit floating point values
    var normalize = false; // normalize the data (convert from 0-255 to 0-1)
    var stride = 0; // 0 = move forward size * sizeof(type) each iteration to get the next position
    var offset = 0; // start at the beginning of the buffer
    gl.vertexAttribPointer(
      normalAttr.loc,
      normalAttr.size,
      type,
      normalize,
      stride,
      offset
    );

    // Compute the projection matrix
    var aspect = gl.canvas.clientWidth / gl.canvas.clientHeight;
    var zNear = 1;
    var zFar = 2000;
    var projectionMatrix = m4.perspective(
      fieldOfViewRadians,
      aspect,
      zNear,
      zFar
    );

    // Compute the camera's matrix
    var camera = [0, 0, 300];
    var target = [0, 0, 0];
    var up = [0, 1, 0];
    var cameraMatrix = m4.lookAt(camera, target, up);

    // Make a view matrix from the camera matrix.
    var viewMatrix = m4.inverse(cameraMatrix);

    // Compute a view projection matrix
    var viewProjectionMatrix = m4.multiply(projectionMatrix, viewMatrix);

    // Draw a figure at the origin
    var worldMatrix = m4.xRotation(rotation[0]);
    worldMatrix = m4.yRotate(worldMatrix, rotation[1]);
    worldMatrix = m4.zRotate(worldMatrix, rotation[2]);

    worldMatrix = m4.translate(
      worldMatrix,
      translation[0],
      translation[1],
      translation[2]
    );

    worldMatrix = m4.scale(worldMatrix, scale[0], scale[1], scale[2]);

    // Multiply the matrices.
    var worldViewProjectionMatrix = m4.multiply(
      viewProjectionMatrix,
      worldMatrix
    );
    var worldInverseMatrix = m4.inverse(worldMatrix);
    var worldInverseTransposeMatrix = m4.transpose(worldInverseMatrix);

    // Set the matrices
    gl.uniformMatrix4fv(
      worldViewProjectionMatrixUniform.loc,
      false,
      worldViewProjectionMatrix
    );
    gl.uniformMatrix4fv(
      worldInverseTransposeMatrixUniform.loc,
      false,
      worldInverseTransposeMatrix
    );

    
    // Set the cube's color
    gl.uniform4fv(colorUniform.loc, [0.3, 0.5, 1, 1]);

    // set the light direction.
    gl.uniform3fv(reverseLightDirectionUniform.loc, m4.normalize([0, 0, 1]));

    // Draw the geometry.
    var primitiveType = gl.TRIANGLES;
    var offset = 0;
    var count = 6 * 2 * 3; // 1 face = 2 triangles. 1 triangle = 3 vertices
    gl.drawArrays(primitiveType, offset, count);
  }


Solution

  • For Ambient We can take the light's color and multiply it with a small constant ambient factor, also multiply this with the object's color.

    For Reflection we calculate a reflection vector by reflecting the light direction around the normal vector. Then we calculate the angular distance between this reflection vector and the view direction.

    viewPos is the postion of the camera
    
    In your vertex shader you can calcuate the FragPos like this.
    FragPos = vec3(u_worldViewProjectionMatrix * vec4(a_position));
    
    Calculate the viewDir in Fragment shader
    vec3 viewDir = normalize(viewPos - FragPos);
    
    
    Fragment Shader
    vec3 CalcDirLight(Light light, vec3 normal, vec3 viewDir)
        {
          // vector to point from the light source towards the fragment's position
           vec3 lightDir = normalize(-light.direction);
           
           // diffuse shading
           float _diff = max(dot(normal, lightDir), 0.0);
           
           // specular shading
           vec3 halfWay = normalize( lightDir + viewDir);
           
           float spec = pow(max(dot(halfWay, normal), 0.0), material.shininess);
           vec3 specular = light.specular * spec * material.specular;
          
           vec3 diffuse = light.diffuse * _diff * material.diffuse ; 
              
           
           vec3 ambient = 0.2 * (light.ambient * material.ambient );
          
            return (ambient + diffuse + specular + material.emission ) ;
          
        }