Modificar el Modelo de Iluminación Local para que admita dos fuentes de luz, y
situar estas dos fuentes de luz en posiciones distintas y con colores distintos
<title>Esfera Iluminacion Phong</title>
 <head>
  <meta charset="UTF-8">
</head> 
<script src="./gl-matrix.js"></script>

<script id="vs-phong" type="x-shader/x-vertex">
#version 300 es

layout(location = 1) in vec3 position;
layout(location = 2) in vec3 normal;
uniform mat4 projection, modelview, normalMat;
out vec3 normalInterp;
out vec3 vertPos;

void main(){
    // Pasar a coordenadas del observador la posicion del punto
    // Para ello, premultiplicamos las coordenadas homogéneas (4D del vértice) por la matriz ModelView
vec4 vertPos4 = modelview * vec4(position, 1.0);
    // Volvemos el vértice a 3D
vertPos = vec3(vertPos4) / vertPos4.w;
    // Obtenemos el valor de la normal en el S.C. del observador
    // Para ello se premultiplica el vector normal del vértice por la matriz normal
normalInterp = vec3(normalMat * vec4(normal, 0.0));
    // la posición 2D del vértice es el resultado de premultiplicar su posición por la matriz de proyección
gl_Position = projection * vertPos4;
}
</script>

<script id="fs-phong" type="x-shader/x-fragment">
#version 300 es
precision mediump float;
in vec3 normalInterp;  // Normal en la Superficie en el S.C. del observador
in vec3 vertPos;       // Posicion del Vértice en el S.C. del observador
uniform float Ka;   // Coeficiente de reflexión ambiente
uniform float Kd;   // Coeficiente de reflexión difusa
uniform float Ks;   // Coeficiente de reflexión especular
uniform float uBrillo; // brillo

// Propiedades del material
uniform vec3 uColorAmbiente;
uniform vec3 uColorDifuso;
uniform vec3 uColorEspecular;
uniform vec3 uLuzPos; // Posición de la luz

// Salida: un color

out vec4 glFragColor;
void main() {
  // Normalizamos el vector de la normal en la superficie
  vec3 N = normalize(normalInterp);
  // Obtenemos el vector director de la luz: normalizando (posición de la luz - posición del vértice)
  vec3 L = normalize(uLuzPos - vertPos);

  // componente lambertiana=coseno entre la Normal y la Dirección de la Luz
  // lambertian debe ser >= 0
  // Ley del coseno de Lambert
  float lambertian = max(dot(N,L), 0.0); 
  
  // calculamos la componente especular
  float specular = 0.0;
  // Si estamos viendo una parte iluminada, lambertian > 0 y habrá brillo  
  if(lambertian > 0.0) { 
    // R = dirección de reflexión, dada la normal y la dirección de la luz
    // Ojo, que la dirección de la luz L está apuntando al objeto
    // Usar funcion REFLECT de GLSL
    
    vec3 R = reflect(-L,N);
    
    // V = Vector director de vista (posición observador (0,0,0) - posición del vértice ) NORMALIZADO    
    vec3 V = normalize(-vertPos);
    
    // Calculamos el coseno del ángulo de reflexion (si es <0, lo dejamos a 0.0)
    float specAngle = max(dot( R, V ), 0.0);  
    
    // el componente especular será ese ángulo elevado a uBrillo
    specular = pow(specAngle, uBrillo);   
   }

// El color del fragmento vendrá dado por la suma de las tres componentes:
// Ka * color ambiente +
// Kd * color difuso * componente lambertiana +
// Ks * color especular * componente especular
    
// glFragColor = vec4(0.6,0.1,0.7,1.0);  // *** LÍNEA A BORRAR CUANDO ESTÉ HECHO 
  glFragColor = vec4(Ka * uColorAmbiente + Kd * uColorDifuso * lambertian + Ks * uColorEspecular * specular , 1.0); 
  
}
</script>

<script type="text/javascript">
    getShaderSource = function(id) {
        return document.getElementById(id).textContent.replace(/^\s+|\s+$/g, '');
    };
    

 
var gl;
var canvas;
var shaderProgram;
var vertices;
var vertexBuffer; 
 
var cubeRotation=0.0;
var projectionMatrix;
var modelViewMatrix;
var normalmatrix;

var t = 0.0; 
var posicionLuz = [1.0, 1.0, -1.0];
var colorAmbiente = [0.2, 0.1, 0.0];
var colorDifuso = [0.8, 0.4, 0.0];
var colorEspecular = [1.0, 1.0, 1.0];
var brillo = 80.0;
var valorKa = 1.0;
var valorKd = 1.0;
var valorKs = 1.0;
var clearColor = [0.0, 0.4,0.7];


    
function createGLContext(canvas) {
  var names = ["webgl2", "webgl","experimental-webgl"];
  var context = null;
  for (var i=0; i < names.length; i++) {
    try {
      context = canvas.getContext(names[i]);
    } catch(e) {}
    if (context) {
      break;
    }
  }
  if (context) {
    context.viewportWidth = canvas.width;
    context.viewportHeight = canvas.height;
  } else {
    alert("Error al crear el contexto WebGL!");
  }
  return context;
}
    
    

function loadShader(type, shaderSource) {
  var shader = gl.createShader(type);
  gl.shaderSource(shader, shaderSource);
  gl.compileShader(shader);
  
  if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
      alert("Error compilando el shader" + gl.getShaderInfoLog(shader));
      gl.deleteShader(shader);   
      return null;
  }
  return shader;  
}

    
function setupShaders() {
  var vertexShaderSource = getShaderSource('vs-phong');       
   var fragmentShaderSource = getShaderSource('fs-phong');    
     
  var vertexShader = loadShader(gl.VERTEX_SHADER, vertexShaderSource);
  var fragmentShader = loadShader(gl.FRAGMENT_SHADER, fragmentShaderSource);
  
  shaderProgram = gl.createProgram();
     
  gl.attachShader(shaderProgram, vertexShader);
  gl.attachShader(shaderProgram, fragmentShader);
  gl.linkProgram(shaderProgram);

  if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) {
     console.log(gl.getProgramInfoLog(program));
     gl.deleteProgram(shaderProgram);

  }

  
   gl.useProgram(shaderProgram); 
    
  // obtenemos la localización de las variables IN del vertex shader
shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram,"position");
shaderProgram.vertexNormalAttribute = gl.getAttribLocation(shaderProgram,"normal");
    
// obtenemos la localización de las variables UNIFORMES del vertex shader
shaderProgram.projectionMatrixUniform = gl.getUniformLocation(shaderProgram, "projection");
shaderProgram.modelViewMatrixUniform = gl.getUniformLocation(shaderProgram, "modelview");
shaderProgram.normalMatrixLoc   = gl.getUniformLocation(shaderProgram, "normalMat");     
shaderProgram.luzPosLoc       = gl.getUniformLocation(shaderProgram, "uLuzPos");
shaderProgram.colorAmbienteLoc   = gl.getUniformLocation(shaderProgram, "uColorAmbiente");
shaderProgram.colorDifusoLoc   = gl.getUniformLocation(shaderProgram, "uColorDifuso");
shaderProgram.colorEspecularLoc  = gl.getUniformLocation(shaderProgram, "uColorEspecular");
shaderProgram.brilloLoc      = gl.getUniformLocation(shaderProgram, "uBrillo");
shaderProgram.kaLoc = gl.getUniformLocation(shaderProgram, "Ka");
shaderProgram.kdLoc = gl.getUniformLocation(shaderProgram, "Kd");
shaderProgram.ksLoc = gl.getUniformLocation(shaderProgram, "Ks");
  
  

}
    
    
function loadVertexData(filename) {
    var data = new Float32Array(0);
    var request = new XMLHttpRequest();
    request.open('GET', filename, false);
    request.send(); //"false" above, will block

    if (request.status != 200) {
      alert("No se pudo cargar el archivo " + filename);

    }else{
      var floatVals = request.responseText.split('\n');
      var numFloats = parseInt(floatVals[0]);
      if(numFloats % (3+2+3) != 0) return data;
      data = new Float32Array(numFloats);
      for(var k = 0; k < numFloats; k++) {
        data[k] = floatVals[k+1];
      }
    }
    return data;
  }

function setupBuffers() {
  
  vertices = loadVertexData("./sphere.txt");
    // Vertices almacenados como x y z u v nx ny nz
    // x1 y1 z1 u1 v1 nx1 ny1 nz1 x2 y2 z2 u2 v2 nx2...
  vertexBuffer = gl.createBuffer();
  gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer); 
  gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
  vertexBuffer.itemSize = 3;
  vertexBuffer.numberOfItems = vertices.length / (3+2+3);
  
    var stride = (3+2+3)*Float32Array.BYTES_PER_ELEMENT;
    if (shaderProgram.vertexPositionAttribute!= -1) {
        // posicion
         var offset = 0;         
         gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute, vertexBuffer.itemSize, gl.FLOAT, false, stride, offset);
         gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute);
    }  
    
    if(shaderProgram.vertexNormalAttribute != -1) {
      // normal
      var offset = 0 + (3+2)*Float32Array.BYTES_PER_ELEMENT;
      gl.vertexAttribPointer(shaderProgram.vertexNormalAttribute, vertexBuffer.itemSize, gl.FLOAT, false, stride, offset);
      gl.enableVertexAttribArray(shaderProgram.vertexNormalAttribute);
    }  
}

function setCamera(){
        const fieldOfView = 45 * Math.PI / 180;   // in radians
        const aspect = gl.canvas.clientWidth / gl.canvas.clientHeight;
        const zNear = 0.1;
        const zFar = 20.0;
        projectionMatrix = mat4.create();

  // note: glmatrix.js always has the first argument
  // as the destination to receive the result.
         mat4.perspective(projectionMatrix,
                   fieldOfView,
                   aspect,
                   zNear,
                   zFar);

  
}
    
    
    
    
    
function draw() { 
  gl.viewport(0, 0, gl.viewportWidth, gl.viewportHeight);
  gl.clearColor(0.0, 0.0, 0.0, 1.0);
  gl.clearDepth(1.0); 
  gl.enable(gl.DEPTH_TEST);
  gl.depthFunc(gl.LEQUAL);
    
  gl.clear(gl.COLOR_BUFFER_BIT|gl.DEPTH_BUFFER_BIT);
  
  
  modelViewMatrix = mat4.create();
  setCamera();
    
  mat4.translate(modelViewMatrix,     //  matriz destino
                 modelViewMatrix,     // matriz a trasladar
                 [0.0, 0.0, -2.0]);  // vector de traslacion
 
    mat4.rotate(modelViewMatrix,  // matriz destino
              modelViewMatrix,  // matriz a rotar
              cubeRotation * .7,// cuanto rotar en radianes
              [0, 1, 0]);       // eje de rotacion
    
    
    var modelviewInv = mat4.create();
    normalmatrix = mat4.create();
    mat4.invert(modelviewInv,modelViewMatrix);
    mat4.transpose(normalmatrix,modelviewInv);
    
   
    gl.uniformMatrix4fv(shaderProgram.projectionMatrixUniform, false, projectionMatrix);
    gl.uniformMatrix4fv(shaderProgram.modelViewMatrixUniform, false, modelViewMatrix);
    
    if(shaderProgram.normalMatrixLoc!=-1) gl.uniformMatrix4fv(shaderProgram.normalMatrixLoc, false, normalmatrix);
     
    if(shaderProgram.luzPosLoc != -1)     gl.uniform3fv(shaderProgram.luzPosLoc, posicionLuz);
    if(shaderProgram.colorAmbienteLoc != -1) gl.uniform3fv(shaderProgram.colorAmbienteLoc, colorAmbiente);
    if(shaderProgram.colorDifusoLoc != -1) gl.uniform3fv(shaderProgram.colorDifusoLoc, colorDifuso);
    if(shaderProgram.colorEspecularLoc != -1) gl.uniform3fv(shaderProgram.colorEspecularLoc, colorEspecular);
    if(shaderProgram.brilloLoc != -1)    gl.uniform1f(shaderProgram.brilloLoc, brillo);
    if(shaderProgram.kaLoc != -1)           gl.uniform1f(shaderProgram.kaLoc, valorKa);
    if(shaderProgram.kdLoc != -1)           gl.uniform1f(shaderProgram.kdLoc, valorKd);
    if(shaderProgram.ksLoc != -1)           gl.uniform1f(shaderProgram.ksLoc, valorKs);
 

 
    gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
    gl.drawArrays(gl.TRIANGLES, 0, vertexBuffer.numberOfItems);
    
    cubeRotation += deltaTime;
    
}

function startup() {
  canvas = document.getElementById("myGLCanvas");
  gl = createGLContext(canvas);
  setupShaders();
    
  setupBuffers();
    
   var then = 0;

  // Dibujar la escena continuamente
  function render(now) {
    now *= 0.001;  // Convertimos milisegundos a segundos
    deltaTime = now - then;
    then = now;

    draw();  

    requestAnimationFrame(render);
  }
  requestAnimationFrame(render);
  
}
</script>

</head>

<body onload="startup();">
  <canvas id="myGLCanvas" width="500" height="500"></canvas>



</body></html>
