/* globals window document mat4 */ const internals = { // Constants kEntrypointElementId: 'entrypoint', // The id of the canvas element where we will render kWidthRatio: 2.76, kHeightRatio: 1, kTargetVerticalResolution: 32 + Math.round(Math.random() * 1024), kFieldOfView: 45, kNearLimit: 0.1, kFarLimit: 100, kBackgroundColor: [0.811, 0.73, 0.48, 1.0], kRotationValues: [Math.random(), Math.random(), Math.random()], kFPS: 30, // Constants: Shaders kVertexShader: ` attribute vec4 aVertexPosition; attribute vec4 aColor; varying vec4 vColor; uniform mat4 uModelViewMatrix; uniform mat4 uProjectionMatrix; void main() { gl_Position = uProjectionMatrix * uModelViewMatrix * aVertexPosition; vColor = aColor; } `, kFragmentShader: ` precision mediump float; varying vec4 vColor; void main() { gl_FragColor = vColor; } `, // Properties: Animation currentTime: 0, // Runs on load, Initializes WebGL and starts the animation loop. onLoad() { const canvas = internals.initializeCanvas(); const { gl, programInfo, buffers } = internals.initializeWebGL(canvas); window.requestAnimationFrame(internals.animate.bind(this, gl, programInfo, buffers)); }, // Gets a canvas element and sets up resizing events. initializeCanvas() { const canvas = document.getElementById(internals.kEntrypointElementId); internals.resizeCanvas(canvas); window.addEventListener('resize', () => internals.resizeCanvas(canvas)); return canvas; }, // Given a canvas, it will initialize the webgl context and set up the shaders. initializeWebGL(canvas) { const gl = canvas.getContext('webgl', { preserveDrawingBuffer: true }); const shaderProgram = internals.initializeShaderProgram(gl, internals.kVertexShader, internals.kFragmentShader); const programInfo = { program: shaderProgram, attribLocations: { vertexPosition: gl.getAttribLocation(shaderProgram, 'aVertexPosition'), vertexColor: gl.getAttribLocation(shaderProgram, 'aColor') }, uniformLocations: { projectionMatrix: gl.getUniformLocation(shaderProgram, 'uProjectionMatrix'), modelViewMatrix: gl.getUniformLocation(shaderProgram, 'uModelViewMatrix') } }; const buffers = internals.initializeBuffers(gl); return { gl, programInfo, buffers }; }, // Methods: Canvas resizeCanvas(canvas) { let width = window.innerWidth; let height = Math.round(width * internals.kHeightRatio / internals.kWidthRatio); if (window.innerHeight < height) { height = window.innerHeight; width = Math.round(height * internals.kWidthRatio / internals.kHeightRatio); console.log(width, height); } canvas.style.width = `${width}px`; canvas.style.height = `${height}px`; canvas.width = internals.kTargetVerticalResolution * internals.kWidthRatio; canvas.height = internals.kTargetVerticalResolution; }, // Methods: Shader / GL Related initializeShaderProgram(gl, vertexShaderSource, fragmentShaderSource) { const vertexShader = internals.loadShader(gl, gl.VERTEX_SHADER, vertexShaderSource); const fragmentShader = internals.loadShader(gl, gl.FRAGMENT_SHADER, fragmentShaderSource); 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 shader program: ${gl.getProgramInfoLog(shaderProgram)}`); return null; } return shaderProgram; }, 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(`Unable to compile shader: ${gl.getShaderInfoLog(shader)}`); gl.deleteShader(shader); return null; } return shader; }, initializeBuffers(gl) { const positionBuffer = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer); const positions = [ -1.0, 1.0, 1.0, // Front-top-left 1.0, 1.0, 1.0, // Front-top-right -1.0, -1.0, 1.0, // Front-bottom-left 1.0, -1.0, 1.0, // Front-bottom-right 1.0, -1.0, -1.0, // Back-bottom-right 1.0, 1.0, 1.0, // Front-top-right 1.0, 1.0, -1.0, // Back-top-right -1.0, 1.0, 1.0, // Front-top-left -1.0, 1.0, -1.0, // Back-top-left -1.0, -1.0, 1.0, // Front-bottom-left -1.0, -1.0, -1.0, // Back-bottom-left 1.0, -1.0, -1.0, // Back-bottom-right -1.0, 1.0, -1.0, // Back-top-left 1.0, 1.0, -1.0 // Back-top-right ]; gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW); return { position: positionBuffer }; }, // Methods: Utility createMatrix() { const matrix = mat4.create(); mat4.translate( matrix, matrix, [-0.0, 0.0, -6.0] ); return matrix; }, // Methods: Animation rotate(matrix) { mat4.rotate(internals.modelViewMatrix, internals.modelViewMatrix, internals.kRotationValues[0], [1, 0, 0]); mat4.rotate(internals.modelViewMatrix, internals.modelViewMatrix, internals.kRotationValues[1], [0, 1, 0]); mat4.rotate(internals.modelViewMatrix, internals.modelViewMatrix, internals.kRotationValues[2], [0, 0, 1]); }, drawScene(gl, programInfo, buffers) { gl.clearColor(...internals.kBackgroundColor); gl.clearDepth(1.0); gl.enable(gl.DEPTH_TEST); gl.depthFunc(gl.LEQUAL); const fieldOfView = internals.kFieldOfView * Math.PI / 180; const aspectRatio = internals.kWidthRatio / internals.kHeightRatio; const projectionMatrix = mat4.create(); mat4.perspective( projectionMatrix, fieldOfView, aspectRatio, internals.kNearLimit, internals.kFarLimit ); internals.modelViewMatrix = internals.modelViewMatrix || internals.createMatrix(); internals.rotate(internals.modelViewMatrix); { const numberOfComponents = 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, numberOfComponents, type, normalize, stride, offset ); gl.enableVertexAttribArray(programInfo.attribLocations.vertexPosition); } { // ATTEMPT TO SEND COLOR DATA const colorBuffer = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer); const colors = [ 1.0, 0.0, 0.5, 1.0, // Front-top-left 1.0, 0.3, 0.8, 1.0, // Front-top-right 1.0, 1.0, 0.7, 1.0, // Front-bottom-left 0.0, 0.3, 0.5, 1.0, // Front-bottom-right 0.0, 0.3, 0.5, 1.0, // Back-bottom-right 1.0, 0.3, 0.8, 1.0, // Front-top-right 1.0, 0.3, 0.8, 1.0, // Back-top-right 1.0, 0.0, 0.5, 1.0, // Front-top-left 1.0, 0.0, 0.5, 1.0, // Back-top-left 1.0, 1.0, 0.7, 1.0, // Front-bottom-left 1.0, 1.0, 0.7, 1.0, // Back-bottom-left 0.0, 0.3, 0.5, 1.0, // Back-bottom-right 1.0, 0.0, 0.5, 1.0, // Back-top-left 1.0, 0.3, 0.8, 1.0 // Back-top-right ]; gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(colors), gl.STATIC_DRAW); const numberOfComponents = 4; const type = gl.FLOAT; const normalize = false; const stride = 0; const offset = 0; gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer); gl.vertexAttribPointer( programInfo.attribLocations.vertexColor, numberOfComponents, type, normalize, stride, offset ); gl.enableVertexAttribArray(programInfo.attribLocations.vertexColor); // END ATTEMPT } gl.useProgram(programInfo.program); gl.uniformMatrix4fv( programInfo.uniformLocations.projectionMatrix, false, projectionMatrix ); gl.uniformMatrix4fv( programInfo.uniformLocations.modelViewMatrix, false, internals.modelViewMatrix ); { const offset = 0; const vertexCount = 14; gl.drawArrays(gl.TRIANGLE_STRIP, offset, vertexCount); } }, animate(gl, programInfo, buffers, time) { const delta = time - internals.currentTime; const interval = 1000 / internals.kFPS; if (delta >= interval) { internals.drawScene(gl, programInfo, buffers, delta); internals.currentTime = time; } window.requestAnimationFrame(internals.animate.bind(this, gl, programInfo, buffers)); } }; window.addEventListener('load', internals.onLoad);