WebGL Point Lighting Issue - PullRequest
       53

WebGL Point Lighting Issue

0 голосов
/ 02 апреля 2020

Я попытался создать вращающийся куб в WebGL и текстурировать его, используя технику наложения текстур, которая, я думаю, работала нормально. Куб вращается, чтобы попытаться продемонстрировать (точечное) освещение, которое я пытался реализовать, но кажется, что оно действует неожиданным образом, и я не совсем уверен, что его вызывает. Любые советы будут с благодарностью, спасибо.

// Vertex shader program
var VSHADER_SOURCE = `
  attribute vec4 a_Position;
  attribute vec4 a_Color;
  attribute vec4 a_Normal;        
  attribute vec2 vertTexCoord;   

  uniform mat4 u_ModelMatrix;
  uniform mat4 u_NormalMatrix;
  uniform mat4 u_ProjMatrix;

  varying vec4 v_Color;
  varying vec3 v_Normal;
  varying vec2 fragTexCoord; 
  varying vec3 v_Position;

  void main() {
    gl_Position = u_ProjMatrix * u_ModelMatrix * a_Position;
    v_Normal = normalize(vec3(u_NormalMatrix * a_Normal));
    v_Color = a_Color;
    fragTexCoord = vertTexCoord;
  }
  `;

// Fragment shader program
var FSHADER_SOURCE = `
  precision mediump float;
  uniform sampler2D sampler;
  varying vec3 v_Normal;
  varying vec3 v_Position;
  varying vec2 fragTexCoord;
  varying vec4 v_Color;

  uniform vec3 u_LightColor;
  uniform vec3 u_LightPosition;
  uniform vec3 u_AmbientLight;
  void main() {
    vec3 normal = normalize(v_Normal);
    vec3 u_LightDirection = normalize(u_LightPosition - v_Position);
    float nDotL = max(dot(u_LightDirection, normal), 0.0);
    vec4 TexColor = texture2D(sampler, fragTexCoord);
    vec3 diffuse;
    diffuse = u_LightColor * TexColor.rgb * nDotL * 1.2;

    vec3 ambient = u_AmbientLight * v_Color.rgb;
    gl_FragColor = vec4(diffuse + ambient, v_Color.a);
  }
  `;

var modelMatrix = new Matrix4(); // The model matrix
var projMatrix = new Matrix4();  // The projection matrix
var g_normalMatrix = new Matrix4();  // Coordinate transformation matrix for normals

var g_xAngle = 0.0;    // The rotation x angle (degrees)

function main() {

  // Retrieve <canvas> element
  var canvas = document.getElementById('webgl');
  canvas.width=document.body.clientWidth;
  canvas.height=document.body.clientHeight;

  // Get the rendering context for WebGL
  gl = getWebGLContext(canvas);
  if (!gl) {
    console.log('Failed to get the rendering context for WebGL');
    return;
  }

  // Initialize shaders
  if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) {
    console.log('Failed to intialize shaders.');
    return;
  }

  // Set clear color and enable hidden surface removal
  gl.clearColor(1, 1, 1, 1.0);
  gl.enable(gl.DEPTH_TEST);

  // Clear color and depth buffer
  gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

  // Get the storage locations of uniform attributes
  u_ModelMatrix = gl.getUniformLocation(gl.program, 'u_ModelMatrix');
  u_NormalMatrix = gl.getUniformLocation(gl.program, 'u_NormalMatrix');
  u_ProjMatrix = gl.getUniformLocation(gl.program, 'u_ProjMatrix');
  u_LightColor = gl.getUniformLocation(gl.program, 'u_LightColor');
  u_LightPosition = gl.getUniformLocation(gl.program, 'u_LightPosition');
  u_AmbientLight = gl.getUniformLocation(gl.program, 'u_AmbientLight');

  if (!u_ModelMatrix || !u_ProjMatrix|| !u_NormalMatrix || !u_LightColor || !u_LightPosition || !u_AmbientLight) { 
    console.log('Failed to get the storage location');
    return;
  }


  // Set the light color (white)
  gl.uniform3f(u_LightColor, 1.0, 1.0, 1.0);
  // Set the light direction (in the world coordinate)
  gl.uniform3f(u_LightPosition, 1, 1, 1);
  // Set the ambient light
  gl.uniform3f(u_AmbientLight, 0.2, 0.2, 0.2);

  projMatrix.setPerspective(30, canvas.width/canvas.height, 1, 100);

  function render(now) {
    now *= 0.001;  // convert to seconds
    g_xAngle = (g_xAngle + 1) % 360
    draw();
    requestAnimationFrame(render);
  }
  requestAnimationFrame(render);
}

function initVertexBuffers(gl) {

  var vertices = new Float32Array([   // Coordinates
    -0.5, 0.5, -0.5,   //top
    -0.5, 0.5, 0.5,  
    0.5, 0.5, 0.5,  
    0.5, 0.5, -0.5,

    -0.5, 0.5, 0.5,  //right
    -0.5,-0.5, 0.5,  
    -0.5,-0.5,-0.5,  
    -0.5, 0.5, -0.5,

    0.5, 0.5, 0.5,   //left
    0.5,-0.5, 0.5,   
    0.5,-0.5,-0.5,   
    0.5, 0.5,-0.5,

    0.5, 0.5, 0.5,  //front
    0.5, -0.5, 0.5,  
    -0.5,-0.5, 0.5,   
    -0.5, 0.5, 0.5,

    0.5, 0.5,-0.5,  //back
    0.5,-0.5,-0.5,  
    -0.5, -0.5,-0.5,   
    -0.5, 0.5,-0.5,

    -0.5,-0.5,-0.5,  //bottom
    -0.5,-0.5, 0.5,   
    0.5,-0.5, 0.5,  
    0.5,-0.5, -0.5,

 ]);

  var texcoords = new Float32Array([
    //u, v : u across, v down. 0,0 top left, 0,1 bottom left etc.
    // Blue
    0.0,  0.0,
    1/3,  0.0,
    1/3,  1/3,
    0,  1/3,

    // Green
    0.0,  1/3,
    1/3,  1/3,
    1/3,  2/3,
    0.0,  2/3,

    // Orange
    1/3,  1/3,
    2/3,  1/3,
    2/3,  2/3,
    1/3,  2/3,

    // Red
    0.0,  2/3,
    1/3,  2/3,
    1/3,  1.0,
    0.0,  1.0,

    // White
    1/3,  2/3,
    2/3,  2/3,
    2/3,  1.0,
    1/3,  1.0,

    // Yellow
    2/3,  2/3,
    1.0,  2/3,
    1.0,  1.0,
    2/3,  1.0,

  ])

  var normals = new Float32Array([    // Normal
    0.0, 0.0, 1.0,   0.0, 0.0, 1.0,   0.0, 0.0, 1.0,   0.0, 0.0, 1.0,  // v0-v1-v2-v3 front
    1.0, 0.0, 0.0,   1.0, 0.0, 0.0,   1.0, 0.0, 0.0,   1.0, 0.0, 0.0,  // v0-v3-v4-v5 right
    0.0, 1.0, 0.0,   0.0, 1.0, 0.0,   0.0, 1.0, 0.0,   0.0, 1.0, 0.0,  // v0-v5-v6-v1 up
   -1.0, 0.0, 0.0,  -1.0, 0.0, 0.0,  -1.0, 0.0, 0.0,  -1.0, 0.0, 0.0,  // v1-v6-v7-v2 left
    0.0,-1.0, 0.0,   0.0,-1.0, 0.0,   0.0,-1.0, 0.0,   0.0,-1.0, 0.0,  // v7-v4-v3-v2 down
    0.0, 0.0,-1.0,   0.0, 0.0,-1.0,   0.0, 0.0,-1.0,   0.0, 0.0,-1.0   // v4-v7-v6-v5 back
  ]);

  var indices = new Uint8Array([
     0, 1, 2,   0, 2, 3,    // front
     4, 5, 6,   4, 6, 7,    // right
     8, 9,10,   8,10,11,    // up
    12,13,14,  12,14,15,    // left
    16,17,18,  16,18,19,    // down
    20,21,22,  20,22,23     // back
 ]);


  // Write the vertex property to buffers (coordinates, colors and normals)
  if (!initArrayBuffer(gl, 'a_Position', vertices, 3, gl.FLOAT)) return -1;
  if (!initArrayBuffer(gl, 'vertTexCoord', texcoords, 2, gl.FLOAT)) return -1; 
  if (!initArrayBuffer(gl, 'a_Normal', normals, 3, gl.FLOAT)) return -1;

  // Write the indices to the buffer object
  var indexBuffer = gl.createBuffer();

  gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
  gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, gl.STATIC_DRAW);

  return indices.length;
}

function initArrayBuffer (gl, attribute, data, num, type) {

  // Create a buffer object
  var buffer = gl.createBuffer();
  if (!buffer) {
    console.log('Failed to create the buffer object');
    return false;
  }
  // Write date into the buffer object
  gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
  gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW);
  // Assign the buffer object to the attribute variable
  var a_attribute = gl.getAttribLocation(gl.program, attribute);
  if (a_attribute < 0) {
    console.log('Failed to get the storage location of ' + attribute);
    return false;
  }
  gl.vertexAttribPointer(a_attribute, num, type, false, 0, 0);
  gl.enableVertexAttribArray(a_attribute);
  gl.bindBuffer(gl.ARRAY_BUFFER, null);

  return true;
}

var g_matrixStack = []; // Array for storing a matrix
function pushMatrix(m) { // Store the specified matrix to the array
  var m2 = new Matrix4(m);
  g_matrixStack.push(m2);
}

function popMatrix() { // Retrieve the matrix from the array
  return g_matrixStack.pop();
}

function draw(){

  gl.uniformMatrix4fv(u_ProjMatrix, false, projMatrix.elements);

  // Clear color and depth buffer
  gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

  var n = initVertexBuffers(gl);
  if (n < 0) {
    console.log('Failed to set the vertex information');
    return;
  }

  var boxTexture = setTexture(gl,'cube');

  modelMatrix.setTranslate(0, 0, -5);  // Translation (No translation is supported here)
  modelMatrix.rotate(g_xAngle, 1, 1, 0); // Rotate along y axis

  // Model the cube
  pushMatrix(modelMatrix);
    modelMatrix.scale(1,1,1); // Scale
    drawCube(gl, u_ModelMatrix, u_NormalMatrix, n, boxTexture);
  modelMatrix = popMatrix();

}

function drawCube(gl, u_ModelMatrix, u_NormalMatrix, n, texture) {
  pushMatrix(modelMatrix);

    // Pass the model matrix to the uniform variable
    gl.uniformMatrix4fv(u_ModelMatrix, false, modelMatrix.elements);

    // Calculate the normal transformation matrix and pass it to u_NormalMatrix
    g_normalMatrix.setInverseOf(modelMatrix);
    g_normalMatrix.transpose();
    gl.uniformMatrix4fv(u_NormalMatrix, false, g_normalMatrix.elements);

    gl.bindTexture(gl.TEXTURE_2D, texture);
    gl.activeTexture(gl.TEXTURE0);
    gl.drawElements(gl.TRIANGLES, n, gl.UNSIGNED_BYTE, 0);

  modelMatrix = popMatrix();
}

1 Ответ

1 голос
/ 03 апреля 2020

Было 3 проблемы с кодом

Проблема № 1: Неверный нормальный расчет

v_Normal = normalize(vec3(u_NormalMatrix * a_Normal));

не будет работать. Это берет vec4 a_Normal с w = 1 (по умолчанию), что означает. Вам нужно либо установить a_Normal.w на 0

v_Normal = normalize(vec3(u_NormalMatrix * vec4(a_Normal,0)));

, либо использовать только часть вращения нормальной матрицы

v_Normal = normalize(mat3(u_NormalMatrix) * a_Normal.xyz);

Примечание: .xyz не понадобится, если a_Normal был объявлен как vec3

Проблема № 2: Нормальные данные были неправильными.

Это было легко проверить, добавив нормаль к позиции в вершинном шейдере

gl_Position = u_ProjMatrix * u_ModelMatrix * (a_Position + vec4(a_Normal.xyz * 0.1, 0));

В этом случае каждая грань куба должна двигаться в направлении нормали. Они не дали понять, что нормали плохие.

Конечно, будучи просто кубом, было бы легко просто посмотреть на данные.

Проблема № 3: Код создает новую текстуру в каждом кадре.

Она должна создать текстуру один раз.

Что касается шагов.

  • Я получил работающий код (размещение рабочего кода полезно).
  • Обнаружено, что ваша математическая библиотека использует градусы вместо радианов (это плохо для ИМО)
  • Пробовал рисовать только с текстурой (работал)

    Примечание: это аргумент за то, что он не запускается, если атрибут и единообразные местоположения не отображаются

    Проблема в том, что я хотел сделать, просто набрать gl_FragColor = TexColor, но как только я это сделаю, код JS не сработает потому что WebGL оптимизирует все формы и строгий код не работает.

    Вероятно, лучше просто напечатать предупреждение, поскольку это значительно облегчает отладку шейдеров.

    Чтобы обойти это, мне пришлось использовать каждый атрибут и форму всегда умножая на ноль.

    gl_FragColor = TexColor + 0. * vec4(diffuse + ambient, v_Color.a);
    

    Также не было никакой причины иметь текстуру в вашем примере. Вы могли бы легко избавиться от кода текстуры или использовать белую текстуру размером 1 пиксель. Есть причина, по которой переполнение стека требует от вас опубликовать минимальный пример.

  • Пробовал рисовать только нормали

    gl_FragColor = normal * 0.5 + 0.5 + 0. * ....
    

    Было трудно увидеть, что происходит, поэтому изменил код так, чтобы он вращался только вокруг Y

    modelMatrix.rotate(g_xAngle, /* 1 */ 0, 1, 0); 
    

    Цвета должны всегда быть одинаковыми, если они обращены в одном направлении. Это не так.

  • Это привело к двум другим проблемам.

const m4 = twgl.m4;

class Matrix4 {
  constructor() {
    this.elements = m4.identity();
  }
  setPerspective(fov, aspect, near, far) {
    m4.perspective(fov * Math.PI / 180, aspect, near, far, this.elements);
  }
  setTranslate(x, y, z) {
    m4.translation([x, y, z], this.elements);
  }
  rotate(angle, x, y, z) {
    m4.axisRotate(this.elements, [x, y, z], angle * Math.PI / 180, this.elements);
  }
  scale(x, y, z) {
    m4.scale(this.elements, [x, y, z], this.elements);
  }
  setInverseOf(src) {
    m4.inverse(src.elements, this.elements);
  }
  transpose() {
    m4.transpose(this.elements, this.elements);
  }
}

{
  const ctx = document.querySelector('#cube').getContext('2d');
  for (let i = 0; i < 9; ++i) {
    ctx.fillStyle = `hsl(${i *360 / 9},100%,50%)`;
    ctx.fillRect(i % 3 * 100, (i / 3 | 0) * 50, 100, 50);
  }
}

let boxTexture;

// Vertex shader program
var VSHADER_SOURCE = `
  attribute vec4 a_Position;
  attribute vec4 a_Color;
  attribute vec4 a_Normal;        
  attribute vec2 vertTexCoord;   

  uniform mat4 u_ModelMatrix;
  uniform mat4 u_NormalMatrix;
  uniform mat4 u_ProjMatrix;

  varying vec4 v_Color;
  varying vec3 v_Normal;
  varying vec2 fragTexCoord; 
  varying vec3 v_Position;

  void main() {
    gl_Position = u_ProjMatrix * u_ModelMatrix * a_Position;
    v_Position = (u_ModelMatrix * a_Position).xyz;
    v_Normal = normalize(mat3(u_ModelMatrix) * a_Normal.xyz);
    v_Color = a_Color + u_NormalMatrix[0][0] * 0.;
    fragTexCoord = vertTexCoord;
  }
  `;

// Fragment shader program
var FSHADER_SOURCE = `
  precision highp float;
  uniform sampler2D sampler;
  varying vec3 v_Normal;
  varying vec3 v_Position;
  varying vec2 fragTexCoord;
  varying vec4 v_Color;

  uniform vec3 u_LightColor;
  uniform vec3 u_LightPosition;
  uniform vec3 u_AmbientLight;
  void main() {
    vec3 normal = normalize(v_Normal);
    vec3 u_LightDirection = normalize(u_LightPosition - v_Position);
    float nDotL = max(dot(u_LightDirection, normal), 0.0);
    vec4 TexColor = texture2D(sampler, fragTexCoord);
    vec3 diffuse;
    diffuse = u_LightColor * TexColor.rgb * nDotL * 1.2;

    vec3 ambient = u_AmbientLight * v_Color.rgb;
    gl_FragColor = vec4(diffuse + ambient, v_Color.a);
  }
  `;

var modelMatrix = new Matrix4(); // The model matrix
var projMatrix = new Matrix4();  // The projection matrix
var g_normalMatrix = new Matrix4();  // Coordinate transformation matrix for normals

var g_xAngle = 0.0;    // The rotation x angle (degrees)

function main() {

  // Retrieve <canvas> element
  var canvas = document.getElementById('webgl');
  canvas.width=document.body.clientWidth;
  canvas.height=document.body.clientHeight;

  // Get the rendering context for WebGL
  gl = getWebGLContext(canvas);
  if (!gl) {
    console.log('Failed to get the rendering context for WebGL');
    return;
  }

  // Initialize shaders
  if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) {
    console.log('Failed to intialize shaders.');
    return;
  }

  // Set clear color and enable hidden surface removal
  gl.clearColor(1, 1, 1, 1.0);
  gl.enable(gl.DEPTH_TEST);

  // Clear color and depth buffer
  gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

  // Get the storage locations of uniform attributes
  u_ModelMatrix = gl.getUniformLocation(gl.program, 'u_ModelMatrix');
  u_NormalMatrix = gl.getUniformLocation(gl.program, 'u_NormalMatrix');
  u_ProjMatrix = gl.getUniformLocation(gl.program, 'u_ProjMatrix');
  u_LightColor = gl.getUniformLocation(gl.program, 'u_LightColor');
  u_LightPosition = gl.getUniformLocation(gl.program, 'u_LightPosition');
  u_AmbientLight = gl.getUniformLocation(gl.program, 'u_AmbientLight');

  if (!u_ModelMatrix || !u_ProjMatrix|| !u_NormalMatrix || !u_LightColor || !u_LightPosition || !u_AmbientLight) { 
    console.log('Failed to get the storage location');
    return;
  }


  // Set the light color (white)
  gl.uniform3f(u_LightColor, 1.0, 1.0, 1.0);
  // Set the light direction (in the world coordinate)
  gl.uniform3f(u_LightPosition, 1, 1, 1);
  // Set the ambient light
  gl.uniform3f(u_AmbientLight, 0.2, 0.2, 0.2);

  projMatrix.setPerspective(30, canvas.width/canvas.height, 1, 100);
  
  boxTexture = setTexture(gl,'cube');

  function render(now) {
    now *= 0.001;  // convert to seconds
    g_xAngle = (g_xAngle + 1) % 360
    draw();
    requestAnimationFrame(render);
  }
  requestAnimationFrame(render);
}

function setTexture(gl, texturetype){
  var texture = gl.createTexture();
  gl.bindTexture(gl.TEXTURE_2D, texture);
  gl.texImage2D(
    gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA,
    gl.UNSIGNED_BYTE, document.getElementById(texturetype)
  );
  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);

  gl.bindTexture(gl.TEXTURE_2D, null);
  return texture;
}

function initVertexBuffers(gl) {
  var vertices = new Float32Array([   // Coordinates
    -0.5, 0.5, -0.5,   //top
    -0.5, 0.5, 0.5,  
    0.5, 0.5, 0.5,  
    0.5, 0.5, -0.5,

    -0.5, 0.5, 0.5,  //right
    -0.5,-0.5, 0.5,  
    -0.5,-0.5,-0.5,  
    -0.5, 0.5, -0.5,

    0.5, 0.5, 0.5,   //left
    0.5,-0.5, 0.5,   
    0.5,-0.5,-0.5,   
    0.5, 0.5,-0.5,

    0.5, 0.5, 0.5,  //front
    0.5, -0.5, 0.5,  
    -0.5,-0.5, 0.5,   
    -0.5, 0.5, 0.5,

    0.5, 0.5,-0.5,  //back
    0.5,-0.5,-0.5,  
    -0.5, -0.5,-0.5,   
    -0.5, 0.5,-0.5,

    -0.5,-0.5,-0.5,  //bottom
    -0.5,-0.5, 0.5,   
    0.5,-0.5, 0.5,  
    0.5,-0.5, -0.5,

 ]);

  var texcoords = new Float32Array([
    //u, v : u across, v down. 0,0 top left, 0,1 bottom left etc.
    // Blue
    0.0,  0.0,
    1/3,  0.0,
    1/3,  1/3,
    0,  1/3,

    // Green
    0.0,  1/3,
    1/3,  1/3,
    1/3,  2/3,
    0.0,  2/3,

    // Orange
    1/3,  1/3,
    2/3,  1/3,
    2/3,  2/3,
    1/3,  2/3,

    // Red
    0.0,  2/3,
    1/3,  2/3,
    1/3,  1.0,
    0.0,  1.0,

    // White
    1/3,  2/3,
    2/3,  2/3,
    2/3,  1.0,
    1/3,  1.0,

    // Yellow
    2/3,  2/3,
    1.0,  2/3,
    1.0,  1.0,
    2/3,  1.0,

  ])

  var normals = new Float32Array([    // Normal
    0.0, 1.0, 0.0,   0.0, 1.0, 0.0,   0.0, 1.0, 0.0,   0.0, 1.0, 0.0,  // v0-v5-v6-v1 up
   -1.0, 0.0, 0.0,  -1.0, 0.0, 0.0,  -1.0, 0.0, 0.0,  -1.0, 0.0, 0.0,  // v1-v6-v7-v2 left
    1.0, 0.0, 0.0,   1.0, 0.0, 0.0,   1.0, 0.0, 0.0,   1.0, 0.0, 0.0,  // v0-v3-v4-v5 right
    0.0, 0.0, 1.0,   0.0, 0.0, 1.0,   0.0, 0.0, 1.0,   0.0, 0.0, 1.0,  // v0-v1-v2-v3 front
    0.0, 0.0,-1.0,   0.0, 0.0,-1.0,   0.0, 0.0,-1.0,   0.0, 0.0,-1.0,   // v4-v7-v6-v5 back
    0.0,-1.0, 0.0,   0.0,-1.0, 0.0,   0.0,-1.0, 0.0,   0.0,-1.0, 0.0,  // v7-v4-v3-v2 down
  ]);

  var indices = new Uint8Array([
     0, 1, 2,   0, 2, 3,    // front
     4, 5, 6,   4, 6, 7,    // right
     8, 9,10,   8,10,11,    // up
    12,13,14,  12,14,15,    // left
    16,17,18,  16,18,19,    // down
    20,21,22,  20,22,23     // back
 ]);


  // Write the vertex property to buffers (coordinates, colors and normals)
  if (!initArrayBuffer(gl, 'a_Position', vertices, 3, gl.FLOAT)) return -1;
  if (!initArrayBuffer(gl, 'vertTexCoord', texcoords, 2, gl.FLOAT)) return -1; 
  if (!initArrayBuffer(gl, 'a_Normal', normals, 3, gl.FLOAT)) return -1;

  // Write the indices to the buffer object
  var indexBuffer = gl.createBuffer();

  gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
  gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, gl.STATIC_DRAW);

  return indices.length;
}

function initArrayBuffer (gl, attribute, data, num, type) {

  // Create a buffer object
  var buffer = gl.createBuffer();
  if (!buffer) {
    console.log('Failed to create the buffer object');
    return false;
  }
  // Write date into the buffer object
  gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
  gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW);
  // Assign the buffer object to the attribute variable
  var a_attribute = gl.getAttribLocation(gl.program, attribute);
  if (a_attribute < 0) {
    console.log('Failed to get the storage location of ' + attribute);
    return false;
  }
  gl.vertexAttribPointer(a_attribute, num, type, false, 0, 0);
  gl.enableVertexAttribArray(a_attribute);
  gl.bindBuffer(gl.ARRAY_BUFFER, null);

  return true;
}

var g_matrixStack = []; // Array for storing a matrix
function pushMatrix(m) { // Store the specified matrix to the array
  var m2 = new Matrix4(m);
  g_matrixStack.push(m2);
}

function popMatrix() { // Retrieve the matrix from the array
  return g_matrixStack.pop();
}

function draw(){

  gl.uniformMatrix4fv(u_ProjMatrix, false, projMatrix.elements);

  // Clear color and depth buffer
  gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

  var n = initVertexBuffers(gl);
  if (n < 0) {
    console.log('Failed to set the vertex information');
    return;
  }

  modelMatrix.setTranslate(0, 0, -5);  // Translation (No translation is supported here)
  modelMatrix.rotate(g_xAngle, 1, 1, 0); // Rotate along y axis

  // Model the cube
  pushMatrix(modelMatrix);
    modelMatrix.scale(1,1,1); // Scale
    drawCube(gl, u_ModelMatrix, u_NormalMatrix, n, boxTexture);
  modelMatrix = popMatrix();

}

function drawCube(gl, u_ModelMatrix, u_NormalMatrix, n, texture) {
  pushMatrix(modelMatrix);

    // Pass the model matrix to the uniform variable
    gl.uniformMatrix4fv(u_ModelMatrix, false, modelMatrix.elements);

    // Calculate the normal transformation matrix and pass it to u_NormalMatrix
    g_normalMatrix.setInverseOf(modelMatrix);
    g_normalMatrix.transpose();
    gl.uniformMatrix4fv(u_NormalMatrix, false, g_normalMatrix.elements);

    gl.bindTexture(gl.TEXTURE_2D, texture);
    gl.activeTexture(gl.TEXTURE0);
    gl.drawElements(gl.TRIANGLES, n, gl.UNSIGNED_BYTE, 0);

  modelMatrix = popMatrix();
}

function getWebGLContext(canvas) { return canvas.getContext('webgl'); }
function initShaders(gl, vs, fs) { 
  // This is BAD!!!!!!! Real WebGL apps have more than one program
  // adding properties to builtins is bad as well.
  gl.program = twgl.createProgram(gl, [vs, fs]); 
  // This is BAD!!!!!!!
  gl.useProgram(gl.program);
  return gl.program;
}

main();
<script src="https://twgljs.org/dist/4.x/twgl-full.min.js"></script>
<canvas id="webgl"></canvas>
<canvas id="cube" style="display: none;"></canvas>

Следует также отметить, что вы не указали цвета вершин, что означает, что окружающий цвет умножается на 0,0,0.

Вы можете найти эти уроки полезными.

...