"use strict"; let cv, gl; let program; let floorProgram; let hVertices; let hVertexBuffer; let cubeVertices; let cubeVertexBuffer; let lineCubeVertices; let lineCubeVertexBuffer; let originVertices; let originVertexBuffer; let floorVertices; let floorVertexBuffer; let cameraVertices; let cameraVertexBuffer; let lastFrame = Date.now(); let interpolateProjection = 0; let interpolateLookAt = 0; let displayMatricesVirtually = true; let virtualRealInterpolation = 1; let invertZAxis = true; let cameraPitch = 0.565; let cameraYaw = 0.375; let cameraDistance = 4; let smoothCameraDistance = 4; let mouseDragging = false; async function init() { cv = document.getElementById("cv"); gl = cv.getContext("webgl"); if (!gl) { window.alert("WebGL not supported"); } // input handling document.getElementById("interpolateProjection").addEventListener("input", (e) => { interpolateProjection = e.target.value; if (interpolateProjection <= 0) { document.getElementById("interpolateLookAt").disabled = ""; } else { document.getElementById("interpolateLookAt").disabled = "yes"; } }); document.getElementById("invertZAxis").addEventListener("input", (e) => { invertZAxis = e.target.checked; }); document.getElementById("interpolateLookAt").addEventListener("input", (e) => { interpolateLookAt = e.target.value; if (interpolateLookAt >= 1) { document.getElementById("interpolateProjection").disabled = ""; } else { document.getElementById("interpolateProjection").disabled = "yes"; } }); document.getElementById("displayMatricesVirtually").addEventListener("input", (e) => { displayMatricesVirtually = e.target.checked; }); document.getElementById("backfaceCulling").addEventListener("input", (e) => { if (e.target.checked) { gl.enable(gl.CULL_FACE); } else { gl.disable(gl.CULL_FACE); } }); cv.addEventListener("mousedown", (e) => { if (e.button == 0) { mouseDragging = true; } }); document.addEventListener("mouseup", (e) => { if (e.button == 0) { mouseDragging = false; } }); document.addEventListener("mouseleave", (e) => { mouseDragging = false; }); document.addEventListener("mousemove", (e) => { if (mouseDragging && displayMatricesVirtually) { cameraYaw += e.movementX / 100; cameraPitch += e.movementY / 100; if (cameraPitch < -Math.PI / 2) cameraPitch = -Math.PI / 2; if (cameraPitch > Math.PI / 2) cameraPitch = Math.PI / 2; } }); cv.addEventListener("wheel", (e) => { cameraDistance += e.deltaY / 100; if (cameraDistance < 1) cameraDistance = 1; if (cameraDistance > 10) cameraDistance = 10; }); // end input handling console.log("compiling shaders..."); program = await buildShaderProgram(gl, "shaders/vertex.glsl", "shaders/fragment.glsl"); floorProgram = await buildShaderProgram(gl, "shaders/floor-vertex.glsl", "shaders/floor-fragment.glsl"); console.log("creating vertex buffer"); hVertices = [ // X // Y // left vertical bar -0.35, 0.6, 0.0, 1.0, 0.3, 0.3, -0.35, -0.6, 0.0, 1.0, 0.3, 0.3, -0.2, 0.6, 0.0, 1.0, 0.3, 0.3, -0.2, 0.6, 0.0, 1.0, 0.3, 0.3, -0.35, -0.6, 0.0, 1.0, 0.3, 0.3, -0.2, -0.6, 0.0, 1.0, 0.3, 0.3, // right vertical bar 0.35, -0.6, 0.0, 1.0, 0.3, 0.3, 0.35, 0.6, 0.0, 1.0, 0.3, 0.3, 0.2, 0.6, 0.0, 1.0, 0.3, 0.3, 0.35, -0.6, 0.0, 1.0, 0.3, 0.3, 0.2, 0.6, 0.0, 1.0, 0.3, 0.3, 0.2, -0.6, 0.0, 1.0, 0.3, 0.3, // middle bar -0.2, 0.1, 0.0, 1.0, 0.3, 0.3, -0.2, -0.1, 0.0, 1.0, 0.3, 0.3, 0.2, 0.1, 0.0, 1.0, 0.3, 0.3, 0.2, 0.1, 0.0, 1.0, 0.3, 0.3, -0.2, -0.1, 0.0, 1.0, 0.3, 0.3, 0.2, -0.1, 0.0, 1.0, 0.3, 0.3, // bottom bar -0.35, -0.7, 0.0, 1.0, 0.3, 0.3, -0.35, -0.9, 0.0, 1.0, 0.3, 0.3, 0.35, -0.9, 0.0, 1.0, 0.3, 0.3, -0.35, -0.7, 0.0, 1.0, 0.3, 0.3, 0.35, -0.9, 0.0, 1.0, 0.3, 0.3, 0.35, -0.7, 0.0, 1.0, 0.3, 0.3, ]; 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, ]; cameraVertices = [ // Cube Body (dark gray) // Front face -1, -1, 1, 0.196, 0.196, 0.196, 1, -1, 1, 0.196, 0.196, 0.196, 1, 1, 1, 0.196, 0.196, 0.196, -1, -1, 1, 0.196, 0.196, 0.196, 1, 1, 1, 0.196, 0.196, 0.196, -1, 1, 1, 0.196, 0.196, 0.196, // Back face -1, -1, -1, 0.196, 0.196, 0.196, 1, 1, -1, 0.196, 0.196, 0.196, 1, -1, -1, 0.196, 0.196, 0.196, -1, -1, -1, 0.196, 0.196, 0.196, -1, 1, -1, 0.196, 0.196, 0.196, 1, 1, -1, 0.196, 0.196, 0.196, // Left face -1, -1, -1, 0.196, 0.196, 0.196, -1, -1, 1, 0.196, 0.196, 0.196, -1, 1, 1, 0.196, 0.196, 0.196, -1, -1, -1, 0.196, 0.196, 0.196, -1, 1, 1, 0.196, 0.196, 0.196, -1, 1, -1, 0.196, 0.196, 0.196, // Right face 1, -1, -1, 0.196, 0.196, 0.196, 1, 1, 1, 0.196, 0.196, 0.196, 1, -1, 1, 0.196, 0.196, 0.196, 1, -1, -1, 0.196, 0.196, 0.196, 1, 1, -1, 0.196, 0.196, 0.196, 1, 1, 1, 0.196, 0.196, 0.196, // Top face -1, 1, -1, 0.196, 0.196, 0.196, -1, 1, 1, 0.196, 0.196, 0.196, 1, 1, 1, 0.196, 0.196, 0.196, -1, 1, -1, 0.196, 0.196, 0.196, 1, 1, 1, 0.196, 0.196, 0.196, 1, 1, -1, 0.196, 0.196, 0.196, // Bottom face -1, -1, -1, 0.196, 0.196, 0.196, 1, -1, 1, 0.196, 0.196, 0.196, -1, -1, 1, 0.196, 0.196, 0.196, -1, -1, -1, 0.196, 0.196, 0.196, 1, -1, -1, 0.196, 0.196, 0.196, 1, -1, 1, 0.196, 0.196, 0.196, // Lens (light gray) // Front face -0.5, -0.5, -1.6, 0.706, 0.706, 0.706, 0.5, 0.5, -1.6, 0.706, 0.706, 0.706, 0.5, -0.5, -1.6, 0.706, 0.706, 0.706, -0.5, -0.5, -1.6, 0.706, 0.706, 0.706, -0.5, 0.5, -1.6, 0.706, 0.706, 0.706, 0.5, 0.5, -1.6, 0.706, 0.706, 0.706, // Back face -0.5, -0.5, -1.0, 0.706, 0.706, 0.706, 0.5, -0.5, -1.0, 0.706, 0.706, 0.706, 0.5, 0.5, -1.0, 0.706, 0.706, 0.706, -0.5, -0.5, -1.0, 0.706, 0.706, 0.706, 0.5, 0.5, -1.0, 0.706, 0.706, 0.706, -0.5, 0.5, -1.0, 0.706, 0.706, 0.706, // Left face -0.5, 0.5, -1.6, 0.706, 0.706, 0.706, -0.5, -0.5, -1.0, 0.706, 0.706, 0.706, -0.5, 0.5, -1.0, 0.706, 0.706, 0.706, -0.5, -0.5, -1.6, 0.706, 0.706, 0.706, -0.5, -0.5, -1.0, 0.706, 0.706, 0.706, -0.5, 0.5, -1.6, 0.706, 0.706, 0.706, // Right face 0.5, 0.5, -1.0, 0.706, 0.706, 0.706, 0.5, -0.5, -1.0, 0.706, 0.706, 0.706, 0.5, 0.5, -1.6, 0.706, 0.706, 0.706, 0.5, 0.5, -1.6, 0.706, 0.706, 0.706, 0.5, -0.5, -1.0, 0.706, 0.706, 0.706, 0.5, -0.5, -1.6, 0.706, 0.706, 0.706, // Top face 0.5, 0.5, -1.6, 0.706, 0.706, 0.706, -0.5, 0.5, -1.0, 0.706, 0.706, 0.706, 0.5, 0.5, -1.0, 0.706, 0.706, 0.706, -0.5, 0.5, -1.6, 0.706, 0.706, 0.706, -0.5, 0.5, -1.0, 0.706, 0.706, 0.706, 0.5, 0.5, -1.6, 0.706, 0.706, 0.706, // Bottom face -0.5, -0.5, -1.0, 0.706, 0.706, 0.706, 0.5, -0.5, -1.0, 0.706, 0.706, 0.706, 0.5, -0.5, -1.6, 0.706, 0.706, 0.706, -0.5, -0.5, -1.0, 0.706, 0.706, 0.706, 0.5, -0.5, -1.6, 0.706, 0.706, 0.706, -0.5, -0.5, -1.6, 0.706, 0.706, 0.706 ]; hVertexBuffer = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, hVertexBuffer); gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(hVertices), gl.STATIC_DRAW); 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); cameraVertexBuffer = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, cameraVertexBuffer); gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(cameraVertices), gl.STATIC_DRAW); // unbind buffer gl.bindBuffer(gl.ARRAY_BUFFER, null); // set clear colour gl.clearColor(...hexToRgb("181825"), 1.0); gl.enable(gl.DEPTH_TEST); gl.enable(gl.BLEND); gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA); // 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; if (displayMatricesVirtually) { virtualRealInterpolation = Math.min(virtualRealInterpolation + deltaTime * 2, 1); } else { virtualRealInterpolation = Math.max(virtualRealInterpolation - deltaTime * 2, 0); } smoothCameraDistance += (cameraDistance - smoothCameraDistance) / 8; updateStats(deltaTime); gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); let identity = []; mat4Identity(identity); // Real Matrices let realProjectionMatrix = []; mat4BuildPerspective(realProjectionMatrix, 90.0 / 180.0 * Math.PI, cv.width / cv.height, 0.1, 30); let realViewMatrix = []; let realCameraPosition = [Math.cos(cameraYaw) * smoothCameraDistance * Math.cos(cameraPitch), Math.sin(cameraPitch) * smoothCameraDistance, Math.sin(cameraYaw) * smoothCameraDistance * Math.cos(cameraPitch)]; let realCameraLookAt = [0, 0, 0]; let realCameraUp = [0, 1, 0]; mat4BuildLookAt(realViewMatrix, realCameraPosition, realCameraLookAt, realCameraUp); mat4Interpolate(realProjectionMatrix, identity, realProjectionMatrix, virtualRealInterpolation * virtualRealInterpolation); mat4Interpolate(realViewMatrix, identity, realViewMatrix, virtualRealInterpolation); gl.useProgram(program); gl.uniformMatrix4fv(gl.getUniformLocation(program, "realProjectionMatrix"), gl.FALSE, new Float32Array(realProjectionMatrix)); gl.uniformMatrix4fv(gl.getUniformLocation(program, "realViewMatrix"), gl.FALSE, new Float32Array(realViewMatrix)); gl.useProgram(floorProgram); gl.uniformMatrix4fv(gl.getUniformLocation(floorProgram, "realProjectionMatrix"), gl.FALSE, new Float32Array(realProjectionMatrix)); // Virtual Matrices gl.useProgram(program); let virtualModelViewLocation = gl.getUniformLocation(program, "virtualModelViewMatrix"); let virtualProjectionLocation = gl.getUniformLocation(program, "virtualProjectionMatrix"); let colorOverrideLocation = gl.getUniformLocation(program, "colorOverride"); let virtualProjectionMatrix = []; mat4BuildPerspective(virtualProjectionMatrix, 90.0 / 180.0 * Math.PI, cv.width / cv.height, 1, 5); if (!invertZAxis) { let zAxisFlipMatrix = []; mat4Identity(zAxisFlipMatrix); zAxisFlipMatrix[10] = -1; mat4Interpolate(zAxisFlipMatrix, identity, zAxisFlipMatrix, virtualRealInterpolation); mat4Multiply(virtualProjectionMatrix, zAxisFlipMatrix, virtualProjectionMatrix); } let inverseVirtualProjectionMatrix = []; mat4Inverse(inverseVirtualProjectionMatrix, virtualProjectionMatrix); let virtualViewMatrix = []; let virtualCameraPosition = [1.2, 1.2, 2]; let virtualCameraLookAt = [0, 0, -1]; let virtualCameraUp = [0, 1, 0]; mat4BuildLookAt(virtualViewMatrix, virtualCameraPosition, virtualCameraLookAt, virtualCameraUp); let inverseVirtualViewMatrix = []; mat4Inverse(inverseVirtualViewMatrix, virtualViewMatrix); mat4Interpolate(virtualProjectionMatrix, identity, virtualProjectionMatrix, interpolateProjection); mat4Interpolate(virtualViewMatrix, identity, virtualViewMatrix, interpolateLookAt); gl.uniformMatrix4fv(virtualProjectionLocation, gl.FALSE, new Float32Array(virtualProjectionMatrix)); let modelMatrix = []; mat4Identity(modelMatrix); let modelViewMatrix = []; mat4Multiply(modelViewMatrix, virtualViewMatrix, modelMatrix); gl.uniformMatrix4fv(virtualModelViewLocation, gl.FALSE, new Float32Array(modelViewMatrix)); // draw scene // draw H gl.useProgram(program); gl.bindBuffer(gl.ARRAY_BUFFER, hVertexBuffer); setAttribPointers(); gl.drawArrays(gl.TRIANGLES, 0, hVertices.length / 6); // draw line cube gl.bindBuffer(gl.ARRAY_BUFFER, lineCubeVertexBuffer); setAttribPointers(); gl.uniform3fv(colorOverrideLocation, [1, 1, 0.5]); gl.drawArrays(gl.LINES, 0, lineCubeVertices.length / 6); gl.uniform3fv(colorOverrideLocation, [0, 0, 0]); // draw solid cube 1 let cube1ModelView = []; mat4Identity(cube1ModelView); mat4Scale(cube1ModelView, cube1ModelView, [0.3, 0.3, 0.3]); mat4RotateY(cube1ModelView, cube1ModelView, 0.3); mat4Translate(cube1ModelView, cube1ModelView, [-0.5, 0.3, -0.7]); mat4Multiply(cube1ModelView, virtualViewMatrix, cube1ModelView); gl.uniformMatrix4fv(virtualModelViewLocation, gl.FALSE, new Float32Array(cube1ModelView)); gl.bindBuffer(gl.ARRAY_BUFFER, cubeVertexBuffer); setAttribPointers(); gl.drawArrays(gl.TRIANGLES, 0, cubeVertices.length / 6); // draw solid cube 2 let cube2ModelView = []; mat4Identity(cube2ModelView); mat4Scale(cube2ModelView, cube2ModelView, [0.4, 0.4, 0.4]); mat4RotateY(cube2ModelView, cube2ModelView, 2.6); mat4Translate(cube2ModelView, cube2ModelView, [1.6, 0.3, -1]); mat4Multiply(cube2ModelView, virtualViewMatrix, cube2ModelView); gl.uniformMatrix4fv(virtualModelViewLocation, gl.FALSE, new Float32Array(cube2ModelView)); gl.bindBuffer(gl.ARRAY_BUFFER, cubeVertexBuffer); setAttribPointers(); gl.drawArrays(gl.TRIANGLES, 0, cubeVertices.length / 6); // draw virtual camera let virtualCameraModelView = []; mat4Identity(virtualCameraModelView); mat4Scale(virtualCameraModelView, virtualCameraModelView, [0.1, 0.1, 0.1]); mat4Translate(virtualCameraModelView, virtualCameraModelView, [0, 0, 0.16]); mat4Multiply(virtualCameraModelView, inverseVirtualViewMatrix, virtualCameraModelView); mat4Multiply(virtualCameraModelView, virtualViewMatrix, virtualCameraModelView); gl.uniformMatrix4fv(virtualModelViewLocation, gl.FALSE, new Float32Array(virtualCameraModelView)); gl.bindBuffer(gl.ARRAY_BUFFER, cameraVertexBuffer); setAttribPointers(); if (displayMatricesVirtually && interpolateProjection < 0.01) { gl.drawArrays(gl.TRIANGLES, 0, cameraVertices.length / 6); } let frustomModelMatrix = []; mat4Multiply(frustomModelMatrix, inverseVirtualViewMatrix, inverseVirtualProjectionMatrix); mat4Multiply(frustomModelMatrix, virtualViewMatrix, frustomModelMatrix); // mat4Copy(inverseVirtualProjectionMatrix, frustomModelMatrix); gl.uniformMatrix4fv(virtualModelViewLocation, gl.FALSE, new Float32Array(frustomModelMatrix)); gl.bindBuffer(gl.ARRAY_BUFFER, lineCubeVertexBuffer); setAttribPointers(); gl.uniform3fv(colorOverrideLocation, [1, 0, 1]); gl.drawArrays(gl.LINES, 0, lineCubeVertices.length / 6); gl.uniform3fv(colorOverrideLocation, [0, 0, 0]); // draw origin gl.uniformMatrix4fv(virtualModelViewLocation, gl.FALSE, new Float32Array(identity)); gl.uniformMatrix4fv(virtualProjectionLocation, gl.FALSE, new Float32Array(identity)); gl.bindBuffer(gl.ARRAY_BUFFER, originVertexBuffer); setAttribPointers(); gl.drawArrays(gl.LINES, 0, originVertices.length / 6); // draw origin cube if (interpolateProjection < 0.99 && interpolateLookAt > 0.01) { gl.bindBuffer(gl.ARRAY_BUFFER, lineCubeVertexBuffer); setAttribPointers(); gl.uniform3fv(colorOverrideLocation, [1, 0, 0]); gl.drawArrays(gl.LINES, 0, lineCubeVertices.length / 6); gl.uniform3fv(colorOverrideLocation, [0, 0, 0]); } // draw floor gl.useProgram(floorProgram); let floorModelView = []; mat4Identity(floorModelView); mat4Scale(floorModelView, floorModelView, [10, 0, 10]); mat4Multiply(floorModelView, realViewMatrix, floorModelView); gl.uniformMatrix4fv(gl.getUniformLocation(floorProgram, "realModelViewMatrix"), gl.FALSE, new Float32Array(floorModelView)); gl.bindBuffer(gl.ARRAY_BUFFER, floorVertexBuffer); setAttribPointers(); gl.drawArrays(gl.TRIANGLES, 0, floorVertices.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);