"use strict"; let cv, gl; let program; let cubeVertices; let cubeVertexBuffer; let lineCubeVertices; let lineCubeVertexBuffer; let originVertices; let originVertexBuffer; let floorVertices; let floorVertexBuffer; let lastFrame = Date.now(); async function init() { cv = document.getElementById("cv"); gl = cv.getContext("webgl"); if (!gl) { window.alert("WebGL not supported"); } console.log("compiling shaders..."); program = await buildShaderProgram(gl, "shaders/vertex.glsl", "shaders/fragment.glsl"); console.log("creating vertex buffer"); cubeVertices = [ // Back face (red) -1, -1, -1, 1, 0.5, 0.5, -1, 1, -1, 1, 0.5, 0.5, 1, 1, -1, 1, 0.5, 0.5, -1, -1, -1, 1, 0.5, 0.5, 1, 1, -1, 1, 0.5, 0.5, 1, -1, -1, 1, 0.5, 0.5, // Front face (green) -1, -1, 1, 0.5, 1, 0.5, 1, -1, 1, 0.5, 1, 0.5, 1, 1, 1, 0.5, 1, 0.5, -1, -1, 1, 0.5, 1, 0.5, 1, 1, 1, 0.5, 1, 0.5, -1, 1, 1, 0.5, 1, 0.5, // Left face (blue) -1, 1, -1, 0.5, 0.5, 1, -1, -1, -1, 0.5, 0.5, 1, -1, 1, 1, 0.5, 0.5, 1, -1, 1, 1, 0.5, 0.5, 1, -1, -1, -1, 0.5, 0.5, 1, -1, -1, 1, 0.5, 0.5, 1, // Right face (yellow) 1, -1, -1, 1, 1, 0.5, 1, 1, -1, 1, 1, 0.5, 1, 1, 1, 1, 1, 0.5, 1, -1, -1, 1, 1, 0.5, 1, 1, 1, 1, 1, 0.5, 1, -1, 1, 1, 1, 0.5, // Top face (magenta) -1, 1, -1, 1, 0.5, 1, -1, 1, 1, 1, 0.5, 1, 1, 1, 1, 1, 0.5, 1, -1, 1, -1, 1, 0.5, 1, 1, 1, 1, 1, 0.5, 1, 1, 1, -1, 1, 0.5, 1, // Bottom face (cyan) -1, -1, 1, 0.5, 1, 1, -1, -1, -1, 0.5, 1, 1, 1, -1, 1, 0.5, 1, 1, 1, -1, 1, 0.5, 1, 1, -1, -1, -1, 0.5, 1, 1, 1, -1, -1, 0.5, 1, 1 ]; lineCubeVertices = [ 1, 1, 1, 1, 0, 0, 1, 1, -1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 1, -1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, -1, 1, 1, 1, 0, 0, 1, 1, -1, 1, 0, 0, 1, -1, -1, 1, 0, 0, 1, 1, -1, 1, 0, 0, -1, 1, -1, 1, 0, 0, 1, -1, 1, 1, 0, 0, 1, -1, -1, 1, 0, 0, 1, -1, 1, 1, 0, 0, -1, -1, 1, 1, 0, 0, -1, 1, 1, 1, 0, 0, -1, 1, -1, 1, 0, 0, -1, 1, 1, 1, 0, 0, -1, -1, 1, 1, 0, 0, 1, -1, -1, 1, 0, 0, -1, -1, -1, 1, 0, 0, -1, -1, 1, 1, 0, 0, -1, -1, -1, 1, 0, 0, -1, 1, -1, 1, 0, 0, -1, -1, -1, 1, 0, 0, ]; originVertices = [ 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, ]; floorVertices = [ 1, 0, 1, 0, 0, 0, -1, 0, -1, 0, 0, 0, -1, 0, 1, 0, 0, 0, -1, 0, -1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, -1, 0, 0, 0, ]; cubeVertexBuffer = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, cubeVertexBuffer); gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(cubeVertices), gl.STATIC_DRAW); lineCubeVertexBuffer = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, lineCubeVertexBuffer); gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(lineCubeVertices), gl.STATIC_DRAW); originVertexBuffer = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, originVertexBuffer); gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(originVertices), gl.STATIC_DRAW); floorVertexBuffer = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, floorVertexBuffer); gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(floorVertices), gl.STATIC_DRAW); // unbind buffer gl.bindBuffer(gl.ARRAY_BUFFER, null); // set clear colour gl.clearColor(...hexToRgb("181825"), 1.0); gl.enable(gl.CULL_FACE); // start drawing requestAnimationFrame(draw); } function setAttribPointers() { let positionAttribLocation = gl.getAttribLocation(program, "vertPosition"); let colorAttribLocation = gl.getAttribLocation(program, "vertColor"); gl.vertexAttribPointer(positionAttribLocation, 3, gl.FLOAT, gl.FALSE, 6 * Float32Array.BYTES_PER_ELEMENT, 0); gl.vertexAttribPointer(colorAttribLocation, 3, gl.FLOAT, gl.FALSE, 6 * Float32Array.BYTES_PER_ELEMENT, 3 * Float32Array.BYTES_PER_ELEMENT); gl.enableVertexAttribArray(positionAttribLocation); gl.enableVertexAttribArray(colorAttribLocation); } let frameCount = 0; let lastStatUpdate = Date.now(); function updateStats(deltaTime) { frameCount++; let now = Date.now(); let timeSinceLastUpdate = (now - lastStatUpdate) / 1000; if (timeSinceLastUpdate < 0.5) return; lastStatUpdate = now; let fpsDisplay = document.getElementById("fps"); fpsDisplay.innerText = Math.round(frameCount / timeSinceLastUpdate) let msDisplay = document.getElementById("ms"); msDisplay.innerText = deltaTime * 1000 + "ms"; frameCount = 0; } let hue = 0; async function draw() { let now = Date.now(); let deltaTime = (now - lastFrame) / 1000; hue += deltaTime / 5; if (hue > 1) hue = 0; updateStats(deltaTime); gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); gl.useProgram(program); let modelViewLocation = gl.getUniformLocation(program, "modelViewMatrix"); let projectionLocation = gl.getUniformLocation(program, "projectionMatrix"); let projectionMatrix = []; mat4BuildPerspective(projectionMatrix, 90.0 / 180.0 * Math.PI, cv.width / cv.height, 0.1, 20); gl.uniformMatrix4fv(projectionLocation, gl.FALSE, new Float32Array(projectionMatrix)); let viewMatrix = []; let cameraPosition = [2, 2, 2]; let cameraLookAt = [0, 0, -1]; let cameraUp = [0, 1, 0]; mat4BuildLookAt(viewMatrix, cameraPosition, cameraLookAt, cameraUp); let modelMatrix = []; mat4Identity(modelMatrix); mat4RotateX(modelMatrix, modelMatrix, hue * 2 * Math.PI); mat4RotateY(modelMatrix, modelMatrix, hue * 2 * Math.PI); let modelViewMatrix = []; mat4Multiply(modelViewMatrix, viewMatrix, modelMatrix); gl.uniformMatrix4fv(modelViewLocation, gl.FALSE, new Float32Array(modelViewMatrix)); gl.bindBuffer(gl.ARRAY_BUFFER, cubeVertexBuffer); setAttribPointers(); gl.drawArrays(gl.TRIANGLES, 0, cubeVertices.length / 6); gl.lineWidth(3); gl.bindBuffer(gl.ARRAY_BUFFER, lineCubeVertexBuffer); setAttribPointers(); gl.drawArrays(gl.LINES, 0, lineCubeVertices.length / 6); gl.uniformMatrix4fv(modelViewLocation, gl.FALSE, new Float32Array(viewMatrix)); gl.bindBuffer(gl.ARRAY_BUFFER, originVertexBuffer); setAttribPointers(); gl.drawArrays(gl.LINES, 0, originVertices.length / 6); lastFrame = now; requestAnimationFrame(draw); } function hexToRgb(hex) { let r = parseInt(hex.substring(0, 2), 16) / 255; let g = parseInt(hex.substring(2, 4), 16) / 255; let b = parseInt(hex.substring(4, 6), 16) / 255; return [r, g, b]; } function hslToRgb(h, s, l) { let r, g, b; if (s == 0) { r = g = b = l; } else { let hue2rgb = function hue2rgb(p, q, t) { if (t < 0) t += 1; if (t > 1) t -= 1; if (t < 1 / 6) return p + (q - p) * 6 * t; if (t < 1 / 2) return q; if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6; return p; } let q = l < 0.5 ? l * (1 + s) : l + s - l * s; let p = 2 * l - q; r = hue2rgb(p, q, h + 1 / 3); g = hue2rgb(p, q, h); b = hue2rgb(p, q, h - 1 / 3); } return [r, g, b]; } window.addEventListener("DOMContentLoaded", init);