// Initialize WebGL context
const canvas = document
.getElementById('webglCanvas');
const gl = canvas
.getContext('webgl');
if (!gl) {
console.error('WebGL not supported, falling back on experimental-webgl');
gl = canvas.getContext('experimental-webgl');
}
if (!gl) {
alert('Your browser does not support WebGL');
}
// Vertex shader program
const vsSource = `
attribute vec4 aVertexPosition;
attribute vec4 aVertexNormal;
uniform mat4 uModelViewMatrix;
uniform mat4 uProjectionMatrix;
uniform mat4 uNormalMatrix;
uniform vec3 uLightDirection;
varying lowp vec4 vColor;
void main(void) {
gl_Position =
uProjectionMatrix * uModelViewMatrix * aVertexPosition;
// Apply lighting
vec3 ambientLight = vec3(0.3, 0.3, 0.3);
vec3 directionalLightColor = vec3(1, 1, 1);
vec3 transformedNormal =
mat3(uNormalMatrix) * aVertexNormal.xyz;
float directional =
max(dot(transformedNormal, uLightDirection), 0.0);
vColor =
vec4(ambientLight + (directionalLightColor * directional), 1.0);
}
`;
// Fragment shader program
const fsSource = `
varying lowp vec4 vColor;
void main(void) {
gl_FragColor = vColor;
}
`;
// Initialize shader program
const shaderProgram =
initShaderProgram(gl, vsSource, fsSource);
// Collect all the info needed to use the shader program.
const programInfo = {
program: shaderProgram,
attribLocations: {
vertexPosition: gl
.getAttribLocation(shaderProgram,
'aVertexPosition'),
vertexNormal: gl
.getAttribLocation(shaderProgram,
'aVertexNormal'),
},
uniformLocations: {
projectionMatrix: gl
.getUniformLocation(shaderProgram,
'uProjectionMatrix'),
modelViewMatrix: gl
.getUniformLocation(shaderProgram,
'uModelViewMatrix'),
normalMatrix: gl
.getUniformLocation(shaderProgram,
'uNormalMatrix'),
lightDirection: gl
.getUniformLocation(shaderProgram,
'uLightDirection'),
},
};
// Define the positions and normals for the cube
const buffers = initBuffers(gl);
let cubeRotation = 0.0;
function render(now) {
now *= 0.001; // convert to seconds
cubeRotation = now;
drawScene(gl, programInfo,
buffers, cubeRotation);
requestAnimationFrame(render);
}
requestAnimationFrame(render);
function initShaderProgram(gl, vsSource, fsSource) {
const vertexShader =
loadShader(gl,
gl.VERTEX_SHADER, vsSource);
const fragmentShader =
loadShader(gl,
gl.FRAGMENT_SHADER, fsSource);
const shaderProgram =
gl.createProgram();
gl.attachShader(shaderProgram,
vertexShader);
gl.attachShader(shaderProgram,
fragmentShader);
gl.linkProgram(shaderProgram);
if (!gl
.getProgramParameter(shaderProgram,
gl.LINK_STATUS)) {
console
.error('Unable to initialize the shader program: '
+ gl.getProgramInfoLog(shaderProgram));
return null;
}
return shaderProgram;
}
function loadShader(gl, type, source) {
const shader = gl
.createShader(type);
gl.shaderSource(shader, source);
gl.compileShader(shader);
if (!gl
.getShaderParameter(shader, gl.COMPILE_STATUS)) {
console.error('An error occurred compiling the shaders: '
+ gl.getShaderInfoLog(shader));
gl.deleteShader(shader);
return null;
}
return shader;
}
function initBuffers(gl) {
// Create a buffer for the cube's vertex positions.
const positionBuffer = gl.createBuffer();
gl.bindBuffer(gl
.ARRAY_BUFFER, positionBuffer);
// Define the positions for the cube
const positions = [
-1.0, -1.0, 1.0,
1.0, -1.0, 1.0,
1.0, 1.0, 1.0,
-1.0, 1.0, 1.0,
-1.0, -1.0, -1.0,
1.0, -1.0, -1.0,
1.0, 1.0, -1.0,
-1.0, 1.0, -1.0,
];
gl.bufferData(gl
.ARRAY_BUFFER,
new Float32Array(positions),
gl.STATIC_DRAW);
// Create a buffer for the cube's vertex normals.
const normalBuffer = gl.createBuffer();
gl.bindBuffer(gl
.ARRAY_BUFFER, normalBuffer);
// Define the normals for the cube
const vertexNormals = [
// 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,
// Back
0.0, 0.0, -1.0,
0.0, 0.0, -1.0,
0.0, 0.0, -1.0,
0.0, 0.0, -1.0,
];
gl.bufferData(gl
.ARRAY_BUFFER,
new Float32Array(vertexNormals),
gl.STATIC_DRAW);
return {
position: positionBuffer,
normal: normalBuffer,
};
}
function drawScene(gl,
programInfo, buffers, rotation) {
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);
const fieldOfView = 45 * Math.PI / 180;
const aspect = gl.canvas
.clientWidth / gl.canvas.clientHeight;
const zNear = 0.1;
const zFar = 100.0;
const projectionMatrix = mat4.create();
mat4
.perspective(projectionMatrix,
fieldOfView, aspect, zNear, zFar);
const modelViewMatrix = mat4.create();
mat4.translate(modelViewMatrix,
modelViewMatrix, [-0.0, 0.0, -6.0]);
mat4.rotate(modelViewMatrix,
modelViewMatrix, rotation, [0, 0, 1]);
mat4.rotate(modelViewMatrix,
modelViewMatrix, rotation * .7, [0, 1, 0]);
const normalMatrix = mat4.create();
mat4.invert(normalMatrix,
modelViewMatrix);
mat4.transpose(normalMatrix,
normalMatrix);
{
const numComponents = 3;
const type = gl.FLOAT;
const normalize = false;
const stride = 0;
const offset = 0;
gl.bindBuffer(gl.ARRAY_BUFFER,
buffers.position);
gl.vertexAttribPointer(programInfo
.attribLocations.vertexPosition,
numComponents, type, normalize, stride, offset);
gl.enableVertexAttribArray(programInfo
.attribLocations.vertexPosition);
}
{
const numComponents = 3;
const type = gl.FLOAT;
const normalize = false;
const stride = 0;
const offset = 0;
gl.bindBuffer(gl.ARRAY_BUFFER, buffers.normal);
gl.vertexAttribPointer(programInfo
.attribLocations.vertexNormal,
numComponents, type, normalize, stride, offset);
gl.enableVertexAttribArray(programInfo
.attribLocations.vertexNormal);
}
gl.useProgram(programInfo.program);
gl.uniformMatrix4fv(programInfo
.uniformLocations
.projectionMatrix,
false, projectionMatrix);
gl.uniformMatrix4fv(programInfo
.uniformLocations.modelViewMatrix,
false, modelViewMatrix);
gl.uniformMatrix4fv(programInfo
.uniformLocations.normalMatrix,
false, normalMatrix);
gl.uniform3fv(programInfo
.uniformLocations
.lightDirection, [-0.3, -0.7, -1.0]);
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 8);
}