title: Shading in webgl
body: <p>I have been trying to create shading in webgl just like in this image: where we can see a cone, a sphere, and a light (which we can change his position with the sliders).</p>
  <p><img src="https://i.stack.imgur.com/NfChR.png" alt="enter image description here" /></p>
  <p>I've tried to write some code in the html file by seeing multiple examples of shading from some webgl tutoring sites, but right now, I can't even see the shapes. It's sure that I'm doing something wrong, but I just don't know where. Here's my code and I also included a link because it contains multiple files. Thanks in advance.</p>
  <p>Link: <a href="https://wetransfer.com/downloads/cd0f66f2e2866c0d118e95b02e01cb0520200923203442/274553" rel="nofollow noreferrer">https://wetransfer.com/downloads/cd0f66f2e2866c0d118e95b02e01cb0520200923203442/274553</a></p>
  <p><div class="snippet" data-lang="js" data-hide="false" data-console="true" data-babel="false">
  <div class="snippet-code">
  <pre class="snippet-code-html lang-html prettyprint-override"><code>&lt;html&gt;
  
  &lt;head&gt;
  &lt;title&gt;Light and Shading&lt;/title&gt;
  &lt;meta http-equiv='content-type' content='text/html; charset=ISO-8859-1'&gt;
  
  &lt;!-- CSS Styles //--&gt;
  &lt;link href='css/style.css'   type='text/css' rel='stylesheet'&gt;
  &lt;link href='css/desert.css'  type='text/css' rel='stylesheet'/&gt;
  &lt;link href='css/colorpicker.css'  type='text/css' rel='stylesheet'/&gt;
  &lt;link href='css/smoothness/jquery-ui-1.8.13.custom.css' type='text/css' rel='stylesheet' /&gt;
  
  &lt;!-- JavaScript Libraries //--&gt;
  &lt;script type='text/javascript' src='js/gl-matrix-min.js'&gt;&lt;/script&gt;
  &lt;script type='text/javascript' src='js/jquery-1.5.1.min.js'&gt;&lt;/script&gt;
  &lt;script type='text/javascript' src='js/jquery-ui-1.8.13.custom.min.js'&gt;&lt;/script&gt; 
  &lt;script type='text/javascript' src='js/prettify.js'&gt;&lt;/script&gt;
  &lt;script type='text/javascript' src='js/utils.js'&gt;&lt;/script&gt;
  &lt;script type='text/javascript' src='js/colorpicker.js'&gt;&lt;/script&gt;
  &lt;script type='text/javascript' src='js/codeview.js'&gt;&lt;/script&gt;
  
  &lt;script id="shader-vs" type="x-shader/x-vertex"&gt;
      attribute vec3 aVertexPosition;
      attribute vec3 aVertexNormal;
  
      // matrice model-view combinee.
      uniform mat4 uMVMatrix;
  
      // matrice de projection
      uniform mat4 uPMatrix;
  
      // matrice des normales.
      uniform mat3 uNMatrix;
  
      // position de la lumiere.
      uniform vec3 uLightPosition;
  
      // La normale transformee
      varying vec3 vNormal;
  
      // la direction vertex-lumiere
      varying vec3 vLightRay;
  
      // la direction camera-vertex
      varying vec3 vEyeVec;
  
      uniform vec4 uLightAmbient;
      uniform vec4 uLightDiffuse;
      uniform vec4 uLightSpecular;
  
      uniform vec4 uMaterialAmbient;
      uniform vec4 uMaterialDiffuse;
      uniform vec4 uMaterialSpecular;
  
  
      void main(void) {
  
  
      vec4 ambientProduct= uLightAmbient* uMaterialAmbient;
      vec4 diffuseProduct= uLightDiffuse*uMaterialDiffuse;
      vec4 specularProduct= uLightSpecular*uMaterialSpecular;
  
      vec3 pos = (uMVMatrix*vec4(aVertexPosition, 1.0)).xyz;
  
  
      // position de l'oeil/camera.
      const vec3 eyePosition = vec3(0,0,-40);
  
      //Transformed normal position
      vNormal = normalize((uNMatrix* aVertexNormal).xyz) ;
  
      //Transformed light position
      vec4 light = uMVMatrix * vec4(uLightPosition,1.0);
  
      vec3 lightPos = (uMVMatrix * light).xyz;
  
      //Light position
      vLightRay = normalize(pos - lightPos);
  
      //Vector Eye
      vEyeVec = -normalize(pos);
  
  
      //Final vertex position
      gl_Position = uMVMatrix*uPMatrix* vec4(aVertexPosition, 1.0);
  
      }
  &lt;/script&gt;
  
  &lt;script id="shader-fs" type="x-shader/x-fragment"&gt;
      #ifdef GL_ES
      precision highp float;
      #endif
  
      varying vec3 vNormal;
      varying vec3 vLightRay;
      varying vec3 vEyeVec;
  
      uniform vec4 ambientProduct;
      uniform vec4 diffuseProduct;
      uniform vec4 specularProduct;
      uniform float uShininess;
  
      void main(void)
      {
  
      vec4 diffuse = max(dot( vNormal,vLightRay), 0.0) * diffuseProduct;
      vec3 H = normalize(vLightRay+vEyeVec);
      vec4 specular =
      pow(max(dot(vNormal, H), 0.0), uShininess) * specularProduct;
      if (dot(vLightRay, vNormal) &lt; 0.0)
      specular = vec4(0.0, 0.0, 0.0, 1.0);
  
      vec4 fColor = ambientProduct + diffuse + specular;
      fColor.a = 1.0;
      gl_FragColor =fColor;
  
      }
  &lt;/script&gt;
  
  
  
  &lt;script id='code-js' type="text/javascript"&gt;
  
  var gl = null; // WebGL context
  var prg = null; // The program (shaders)
  var c_width = 0; // Variable to store the width of the canvas
  var c_height = 0; // Variable to store the height of the canvas
  
  var mvMatrix = mat4.create(); // The Model-View matrix
  var pMatrix = mat4.create(); // The projection matrix
  var nMatrix =  mat4.create();      // The normal matrix
  
  var distance = -40;
  var animateFlag = false;
  
  var objects = [];   
  
  /**
  * The program contains a series of instructions that tell the Graphic Processing Unit (GPU)
  * what to do with every vertex and fragment that we pass it. 
  * The vertex shader and the fragment shader together are called the program.
  */
  function initProgram() {
  var fragmentShader      = utils.getShader(gl, "shader-fs");
  var vertexShader        = utils.getShader(gl, "shader-vs");
  
  prg = gl.createProgram();
  gl.attachShader(prg, vertexShader);
  gl.attachShader(prg, fragmentShader);
  gl.linkProgram(prg);
  
  if (!gl.getProgramParameter(prg, gl.LINK_STATUS)) {
  alert("Could not initialise shaders");
  }
  
  gl.useProgram(prg);
  
  prg.aVertexPosition     = gl.getAttribLocation(prg, "aVertexPosition");
  prg.aVertexNormal       = gl.getAttribLocation(prg, "aVertexNormal");
  
  prg.uPMatrix            = gl.getUniformLocation(prg, "uPMatrix");
  prg.uMVMatrix           = gl.getUniformLocation(prg, "uMVMatrix");
  prg.uNMatrix            = gl.getUniformLocation(prg, "uNMatrix");
  
  
  prg.uMaterialAmbient    = gl.getUniformLocation(prg, "uMaterialAmbient");
  prg.uMaterialDiffuse    = gl.getUniformLocation(prg, "uMaterialDiffuse");
  prg.uMaterialSpecular   = gl.getUniformLocation(prg, "uMaterialSpecular");
  prg.uShininess          = gl.getUniformLocation(prg, "uShininess");
  
  
  prg.uLightPosition      = gl.getUniformLocation(prg, "uLightPosition");
  prg.uLightAmbient       = gl.getUniformLocation(prg, "uLightAmbient");
  prg.uLightDiffuse       = gl.getUniformLocation(prg, "uLightDiffuse");
  prg.uLightSpecular      = gl.getUniformLocation(prg, "uLightSpecular");
  }
  
  
  function initLights(){
  //Light uniforms
  gl.uniform3fv(prg.uLightPosition,[4.5,3.0,15.0]);        
  gl.uniform4f(prg.uLightAmbient ,1.0,1.0,1.0,1.0);
  gl.uniform4f(prg.uLightDiffuse,1.0,1.0,1.0,1.0);
  gl.uniform4f(prg.uLightSpecular,1.0,1.0,1.0,1.0);
  
  //Object Uniforms
  gl.uniform4f(prg.uMaterialAmbient, 0.1,0.1,0.1,1.0);
  gl.uniform4f(prg.uMaterialDiffuse, 0.5,0.8,0.1,1.0);
  gl.uniform4f(prg.uMaterialSpecular, 0.6,0.6,0.6,1.0);
  gl.uniform1f(prg.uShininess, 200.0);
  
  }
  
  
  /**
  * Creates an AJAX request to load the scene asynchronously
  */
  function loadScene(){
  loadObject('models/plane.json');
  loadObject('models/cone.json','cone');
  loadObject('models/sphere.json','sphere');
  loadObject('models/smallsph.json','lightsource');
  }
  
  function getObject(alias){
  for(var i=0; i&lt;objects.length; i++){
  if (alias == objects[i].alias) return objects[i];
  }
  return null;
  }
  
  /**
  * Ajax and JSON in action
  */ 
  
  function loadObject(filename,alias){
      var request = new XMLHttpRequest();
      console.info('Requesting ' + filename);
      request.open("GET",filename);
      
      request.onreadystatechange = function() {
      if (request.readyState == 4) {
          if(request.status == 404) {
              console.info(filename + ' does not exist');
          }
          else {
  var o = JSON.parse(request.responseText);
  o.alias = (alias==null)?'none':alias;
              handleLoadedObject(filename,o);
          }
      }
      }
      request.send();
  }
  
  /**
  * Creates the buffers that contain the geometry of the object
  */
  function handleLoadedObject(filename,object) {
      
      console.info(filename + ' has been retrieved from the server');
      
      var vertexBufferObject = gl.createBuffer();
      gl.bindBuffer(gl.ARRAY_BUFFER, vertexBufferObject);
      gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(object.vertices), gl.STATIC_DRAW);
      
          
      var normalBufferObject = gl.createBuffer();
      gl.bindBuffer(gl.ARRAY_BUFFER, normalBufferObject);
      gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(calcNormals(object.vertices, object.indices)), gl.STATIC_DRAW);
      
      var indexBufferObject = gl.createBuffer();
      gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBufferObject);
      gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(object.indices), gl.STATIC_DRAW);
          
      
      object.vbo = vertexBufferObject;
      object.ibo = indexBufferObject;
      object.nbo = normalBufferObject;
  
      gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null);
      gl.bindBuffer(gl.ARRAY_BUFFER,null);
      
      objects.push(object);
  } 
  
  /**
  * Main rendering function. Called every 500ms according to WebGLStart function (see below)
  */
  function drawScene() {
      gl.clearColor(0.3,0.3,0.3, 1.0);
      gl.clearDepth(100.0);
      gl.enable(gl.DEPTH_TEST);
      gl.depthFunc(gl.LEQUAL);
      gl.viewport(0, 0, c_width, c_height);
      gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
      mat4.perspective(60, c_width / c_height, 0.1, 1000.0, pMatrix);
  
      try{
          gl.enableVertexAttribArray(prg.aVertexPosition);
          gl.enableVertexAttribArray(prg.aVertexNormal);
          for (var i = 0; i &lt; objects.length; i++){
              var object = objects[i];
              mat4.identity(mvMatrix);
              mat4.translate(mvMatrix, [0.0, 0.0, distance]); //Sets the camera to a reasonable distance to view the part
              mat4.rotate(mvMatrix, 30*Math.PI/180, [1,0,0]);
              mat4.rotate(mvMatrix, angle*Math.PI/180, [0,1,0]);
              if (object.alias == 'lightsource'){
                  var lightPos = gl.getUniform(prg, prg.uLightPosition);
                  mat4.translate(mvMatrix,lightPos);
                  
              }
              
              gl.uniformMatrix4fv(prg.uMVMatrix, false, mvMatrix);
              gl.uniformMatrix4fv(prg.uPMatrix, false, pMatrix);
              mat4.set(mvMatrix, nMatrix);
              mat4.inverse(nMatrix);
              mat4.transpose(nMatrix);
              
              gl.uniformMatrix4fv(prg.uNMatrix, false, nMatrix);
              gl.uniform4fv(prg.uMaterialAmbient, object.ambient);
              gl.uniform4fv(prg.uMaterialDiffuse, object.diffuse);
              gl.uniform4fv(prg.uMaterialSpecular, object.specular);
              gl.bindBuffer(gl.ARRAY_BUFFER, object.vbo);
              gl.vertexAttribPointer(prg.aVertexPosition, 3, gl.FLOAT, false, 0, 0);
              gl.enableVertexAttribArray(prg.aVertexPosition);
              
              gl.bindBuffer(gl.ARRAY_BUFFER, object.nbo);
              gl.vertexAttribPointer(prg.aVertexNormal, 3, gl.FLOAT, false, 0, 0);
              gl.enableVertexAttribArray(prg.aVertexNormal);
              
              gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, object.ibo);
              gl.drawElements(gl.TRIANGLES, object.indices.length, gl.UNSIGNED_SHORT,0);
              gl.bindBuffer(gl.ARRAY_BUFFER, null);
              gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null);
          }
      }
      catch(err){
          alert(err);
          message(err.description);
      }
  }
  
  
  
  
  
  
  var lastTime = 0;
  var angle = 0;
  /**
  * Updates the angle of rotation by a little bit each time
  */
  function animate() {
      var timeNow = new Date().getTime();
      if (lastTime != 0) {
          var elapsed = timeNow - lastTime;
          if (animateFlag) angle += (90 * elapsed) / 10000.0;
      }
      lastTime = timeNow;
  }
  
  /**
  * Render Loop
  */
  function renderLoop() {
      requestAnimFrame(renderLoop);
      drawScene();
  animate();
  }
  
  /**
  * Entry point. This function is invoked when the page is loaded
  */
  function runWebGLApp() {
  //Obtains a WebGL context
  gl = utils.getGLContext("canvas-element-id");
  //Initializes the program (shaders) 
      initProgram();
      //Initializes lights
      initLights();
      //Load Scene
  loadScene();
      //Renders the scene!
      renderLoop();
  }
  &lt;/script&gt;
  &lt;/head&gt;
  
  &lt;body onLoad='runWebGLApp()'&gt;
  &lt;div id='top'&gt;
  
  &lt;div id='contents'&gt;
  &lt;div id='canvasContainer'&gt;
  &lt;canvas id='canvas-element-id' width='480' height='400'&gt;
  Your browser does not support the HTML5 canvas element.
  &lt;/canvas&gt;
  &lt;/div&gt;
  &lt;/div&gt;
  
  &lt;div id='bottom'&gt;
  &lt;table style='padding=0px'&gt;
  &lt;tr&gt;
  &lt;td&gt;X:&lt;/td&gt;&lt;td id='slider-x-value' width='30px'&gt;4.5&lt;/td&gt;&lt;td width='150px'&gt;&lt;div id='slider-x'/&gt;&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
  &lt;td&gt;Y:&lt;/td&gt;&lt;td id='slider-y-value'  width='30px'&gt;3.0&lt;/td&gt;&lt;td width='150px'&gt;&lt;div id='slider-y'/&gt;&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
  &lt;td&gt;Z:&lt;/td&gt; &lt;td id='slider-z-value'  width='30px'&gt;15.0&lt;/td&gt;&lt;td width='150px'&gt;&lt;div id='slider-z'/&gt;&lt;/td&gt;
  &lt;/tr&gt;
  &lt;/table&gt;
  &lt;/div&gt;
  &lt;script&gt;cview.run(cview.MODE_VIEW);&lt;/script&gt;
  &lt;script&gt; 
  $('#slider-shininess').slider({value:200, min:1, max:300, step:1, slide:updateShininess});
  $('#slider-x').slider({value:4.5, min:-50, max:50, step:0.1, slide:updateLightPosition, change:updateLightPosition});
  $('#slider-y').slider({value:3.0, min:0, max:50, step:0.1, slide:updateLightPosition, change:updateLightPosition});
  $('#slider-z').slider({value:15.0, min:-50, max:50, step:0.1, slide:updateLightPosition, change:updateLightPosition});
  
  
  $('#animate-btn').button();
  $('#animate-btn').click(
  function(){
      if ($('#animate-btn:checked').val()==null){
          animateFlag = false;
      }
      else{
          animateFlag = true;
      }
  });
  function updateShininess(){
      var v = $('#slider-shininess').slider("value");
      gl.uniform1f(prg.uShininess, v);
      $('#slider-shininess-value').html(v);
  }
  
  
  
  function updateLightPosition(){
      var x = $('#slider-x').slider("value");
      var y = $('#slider-y').slider("value");
      var z = $('#slider-z').slider("value");
      gl.uniform3fv(prg.uLightPosition, [x,y,z]);
      $('#slider-x-value').html(x);
      $('#slider-y-value').html(y);
      $('#slider-z-value').html(z);
  }
  
  
  function updateDistance(){
      var d = $('#slider-distance').slider("value");
      $('#slider-distance-value').html(distance);
      distance = -d;
  }
  
  
  
  function updateObjectColor(alias, r,g,b){
      var object = getObject(alias);
      if (object != null){
          object.diffuse = [r,g,b,1.0];
      }
  
  }
  
  $('#colorSelectorSphere').ColorPicker({
  onSubmit: function(hsb, hex, rgb, el) {
          $(el).val(hex);
          $(el).ColorPickerHide();
  
      },
  color: '#00ff00',
  onShow: function (colpkr) {
          $(colpkr).fadeIn(500);
          return false;
      },
  onHide: function (colpkr) {
          $(colpkr).fadeOut(500);
          return false;
      },
  onChange: function (hsb, hex, rgb) {
          $('#colorSelectorSphere div').css('backgroundColor', '#' + hex);
          updateObjectColor('sphere',rgb.r/256,rgb.g/256,rgb.b/256);
      },
      
  onBeforeShow: function (colpkr) {
          $(this).ColorPickerSetColor('rgb(0.5,0.8,0.1)');
      }
  })
  
  $('#colorSelectorCone').ColorPicker({
  onSubmit: function(hsb, hex, rgb, el) {
          $(el).val(hex);
          $(el).ColorPickerHide();
  
      },
  color: '#00ff00',
  onShow: function (colpkr) {
          $(colpkr).fadeIn(500);
          return false;
      },
  onHide: function (colpkr) {
          $(colpkr).fadeOut(500);
          return false;
      },
  onChange: function (hsb, hex, rgb) {
          $('#colorSelectorCone div').css('backgroundColor', '#' + hex);
          updateObjectColor('cone',rgb.r/256,rgb.g/256,rgb.b/256);
      },
      
  onBeforeShow: function (colpkr) {
          $(this).ColorPickerSetColor('rgb(0.8,0.1,0.5)');
      }
  }) 
  
  
  // Calcule les normales des vertex. La normale de chaque vertex est
  // la moyenne des triangles voisins.
  //
  // vertices: la liste des vertex.
  // ind: la liste des indices.
  // retour: la liste des normales par vertex.
  function calcNormals(vertices, ind){
    var x=0; 
    var y=1;
    var z=2;
      var v1 = [], v2 = [], thisNormal = [];
  
      // initialiser la liste des normales.
    var ns = [];
    for(var i=0;i&lt;vertices.length;i++)
      {
      ns[i]=0.0;
    }
  
    for(var i=0;i&lt;ind.length;i=i+3){
      //v1 = p1 - p0
      v1[x] = vertices[3*ind[i+1]+x] - vertices[3*ind[i]+x];
      v1[y] = vertices[3*ind[i+1]+y] - vertices[3*ind[i]+y];
      v1[z] = vertices[3*ind[i+1]+z] - vertices[3*ind[i]+z];
  
      // v2 = p2 - p1
      v2[x] = vertices[3*ind[i+2]+x] - vertices[3*ind[i]+x];
      v2[y] = vertices[3*ind[i+2]+y] - vertices[3*ind[i]+y];
      v2[z] = vertices[3*ind[i+2]+z] - vertices[3*ind[i]+z];            
  
      // N = v2 x v1 (cross product).
      thisNormal[x] = v1[y]*v2[z] - v1[z]*v2[y];
      thisNormal[y] = v1[z]*v2[x] - v1[x]*v2[z];
      thisNormal[z] = v1[x]*v2[y] - v1[y]*v2[x];
  
      for(j=0;j&lt;3;j++)
          {
              // N += thisNormal. on additionne les normales. 
        ns[3*ind[i+j]+x] =  ns[3*ind[i+j]+x] + thisNormal[x];
        ns[3*ind[i+j]+y] =  ns[3*ind[i+j]+y] + thisNormal[y];
        ns[3*ind[i+j]+z] =  ns[3*ind[i+j]+z] + thisNormal[z];
      }
    }
  
    // Normalisation.
    for(var i=0;i&lt;vertices.length;i=i+3){ 
  
      var nn=[];
          var len = 0;
          for(var j = 0; j &lt; 3; j++)
          {
              nn[j] = ns[i+j];
              len += nn[j] * nn[j];
          }
  
          // La norme de la normale.
          len = Math.sqrt(len);
      if (len == 0) 
              len = 0.00001;
  
          for(var j = 0; j &lt; 3; j++)
          ns[i+j] = nn[j] / len;
  
          console.log(len);
    }
  
    return ns;
  }
  
  &lt;/script&gt;
  &lt;/body&gt;
  &lt;/html&gt;</code></pre>
  </div>
  </div>
  </p>