From 067652a2b30fb591c51706ffd12bf140dabbd3b4 Mon Sep 17 00:00:00 2001 From: Hugo Bedard Date: Tue, 11 Feb 2020 20:29:39 -0500 Subject: [PATCH] First commit, rotating cube over plane --- .eslintignore | 2 + .eslintrc.json | 27 ++++++ .gitignore | 6 ++ index.html | 15 +++ package.json | 25 +++++ rollup.config.js | 18 ++++ src/cube.js | 197 ++++++++++++++++++++++++++++++++++++++ src/main.js | 240 +++++++++++++++++++++++++++++++++++++++++++++++ src/plane.js | 116 +++++++++++++++++++++++ webgl.css | 4 + 10 files changed, 650 insertions(+) create mode 100644 .eslintignore create mode 100644 .eslintrc.json create mode 100644 .gitignore create mode 100644 index.html create mode 100644 package.json create mode 100644 rollup.config.js create mode 100644 src/cube.js create mode 100644 src/main.js create mode 100644 src/plane.js create mode 100644 webgl.css diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 0000000..282bfb6 --- /dev/null +++ b/.eslintignore @@ -0,0 +1,2 @@ +dist/* +rollup.config.js diff --git a/.eslintrc.json b/.eslintrc.json new file mode 100644 index 0000000..3637e9a --- /dev/null +++ b/.eslintrc.json @@ -0,0 +1,27 @@ +{ + "extends": "eslint:recommended", + "parserOptions": { + "ecmaVersion": 2017, + "sourceType": "module" + }, + "globals":{ + "axios": false, + "dat": false, + "Stats": false + }, + "env": { + "browser": true, + "commonjs": true, + "es6": true, + "node": true + }, + "rules": { + "indent": 1, + "semi": 1, + "no-extra-semi": 1, + "new-cap": ["warn", { "newIsCapExceptionPattern": "^gl" }], + "no-console": [ + "off" + ] + } +} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d48c77a --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +node_modules +npm-debug.log +.vscode/launch.json +.vscode/tasks.json +package-lock.json +dist diff --git a/index.html b/index.html new file mode 100644 index 0000000..1790ed4 --- /dev/null +++ b/index.html @@ -0,0 +1,15 @@ + + + + + WebGL Demo + + + + + + + + + + diff --git a/package.json b/package.json new file mode 100644 index 0000000..355b30f --- /dev/null +++ b/package.json @@ -0,0 +1,25 @@ +{ + "name": "webglsamples", + "version": "1.0.0", + "description": "To do, enter some description", + "main": "main.js", + "dependencies": { + "gl-matrix": "^3.1.0" + }, + "devDependencies": { + "eslint": "^5.10.0", + "rollup": "^0.67.4", + "rollup-plugin-commonjs": "^9.3.4", + "rollup-plugin-glslify": "^1.1.0", + "rollup-plugin-node-resolve": "^3.4.0" + }, + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1", + "dev": "concurrently \"rollup -c -w\" \"serve --listen 8000 .\"", + "lint": "eslint src/**/*.js src/*.js", + "lint:fix": "eslint --fix src/**/*.js src/*.js" + }, + "author": "", + "license": "ISC", + "repository": "todoenterrepository" +} diff --git a/rollup.config.js b/rollup.config.js new file mode 100644 index 0000000..71dbb81 --- /dev/null +++ b/rollup.config.js @@ -0,0 +1,18 @@ +import commonjs from 'rollup-plugin-commonjs'; +import glslify from 'rollup-plugin-glslify'; +import resolve from 'rollup-plugin-node-resolve'; + +export default { + input: 'src/main.js', + output: [ + { + file: 'dist/bundle.js', + format: 'cjs' + } + ], + plugins: [ + glslify(), + resolve(), + commonjs() + ] +}; diff --git a/src/cube.js b/src/cube.js new file mode 100644 index 0000000..230d8df --- /dev/null +++ b/src/cube.js @@ -0,0 +1,197 @@ +import { mat4 } from 'gl-matrix'; + +class Cube +{ + constructor(gl) { + this.gl = gl; + this.createPositionBuffer(); + this.createIndexBuffer(); + this.createNormalBuffer(); + this.rotation = 0.0; + } + + update(deltaTime) { + this.modelMatrix = mat4.create(); + + mat4.rotate(this.modelMatrix, // destination matrix + this.modelMatrix, // matrix to rotate + this.rotation, // amount to rotate in radians + [0, 0, 1]); // axis to rotate around (Z) + mat4.rotate(this.modelMatrix, // destination matrix + this.modelMatrix, // matrix to rotate + this.rotation * .7,// amount to rotate in radians + [0, 1, 0]); // axis to rotate around (X) + + this.rotation += deltaTime; + } + + draw(programInfo) { + this.gl.uniformMatrix4fv( + programInfo.uniformLocations.modelMatrix, + false, + this.modelMatrix); + + // Tell WebGL how to pull out the positions from the position + // buffer into the vertexPosition attribute + { + const numComponents = 3; + const type = this.gl.FLOAT; + const normalize = false; + const stride = 0; + const offset = 0; + this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.positionBuffer); + this.gl.vertexAttribPointer( + programInfo.attribLocations.vertexPosition, + numComponents, + type, + normalize, + stride, + offset); + this.gl.enableVertexAttribArray( + programInfo.attribLocations.vertexPosition); + } + + // Tell WebGL how to pull out the normals from + // the normal buffer into the vertexNormal attribute. + { + const numComponents = 3; + const type = this.gl.FLOAT; + const normalize = false; + const stride = 0; + const offset = 0; + this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.normalBuffer); + this.gl.vertexAttribPointer( + programInfo.attribLocations.vertexNormal, + numComponents, + type, + normalize, + stride, + offset); + this.gl.enableVertexAttribArray( + programInfo.attribLocations.vertexNormal); + } + + // Tell WebGL which indices to use to index the vertices + this.gl.bindBuffer(this.gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer); + + { + const vertexCount = 36; + const type = this.gl.UNSIGNED_SHORT; + const offset = 0; + this.gl.drawElements(this.gl.TRIANGLES, vertexCount, type, offset); + } + } + + createPositionBuffer() { + this.positionBuffer = this.gl.createBuffer(); + + this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.positionBuffer); + + const positions = [ + // Front face + -1.0, -1.0, 1.0, + 1.0, -1.0, 1.0, + 1.0, 1.0, 1.0, + -1.0, 1.0, 1.0, + + // Back face + -1.0, -1.0, -1.0, + -1.0, 1.0, -1.0, + 1.0, 1.0, -1.0, + 1.0, -1.0, -1.0, + + // Top face + -1.0, 1.0, -1.0, + -1.0, 1.0, 1.0, + 1.0, 1.0, 1.0, + 1.0, 1.0, -1.0, + + // Bottom face + -1.0, -1.0, -1.0, + 1.0, -1.0, -1.0, + 1.0, -1.0, 1.0, + -1.0, -1.0, 1.0, + + // Right face + 1.0, -1.0, -1.0, + 1.0, 1.0, -1.0, + 1.0, 1.0, 1.0, + 1.0, -1.0, 1.0, + + // Left face + -1.0, -1.0, -1.0, + -1.0, -1.0, 1.0, + -1.0, 1.0, 1.0, + -1.0, 1.0, -1.0, + ]; + + this.gl.bufferData(this.gl.ARRAY_BUFFER, new Float32Array(positions), this.gl.STATIC_DRAW); + } + + createIndexBuffer() { + this.indexBuffer = this.gl.createBuffer(); + + this.gl.bindBuffer(this.gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer); + + const indices = [ + 0, 1, 2, 0, 2, 3, // front + 4, 5, 6, 4, 6, 7, // back + 8, 9, 10, 8, 10, 11, // top + 12, 13, 14, 12, 14, 15, // bottom + 16, 17, 18, 16, 18, 19, // right + 20, 21, 22, 20, 22, 23, // left + ]; + + this.gl.bufferData(this.gl.ELEMENT_ARRAY_BUFFER, + new Uint16Array(indices), this.gl.STATIC_DRAW); + } + + createNormalBuffer() { + this.normalBuffer = this.gl.createBuffer(); + + this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.normalBuffer); + + 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, + + // Top + 0.0, 1.0, 0.0, + 0.0, 1.0, 0.0, + 0.0, 1.0, 0.0, + 0.0, 1.0, 0.0, + + // Bottom + 0.0, -1.0, 0.0, + 0.0, -1.0, 0.0, + 0.0, -1.0, 0.0, + 0.0, -1.0, 0.0, + + // Right + 1.0, 0.0, 0.0, + 1.0, 0.0, 0.0, + 1.0, 0.0, 0.0, + 1.0, 0.0, 0.0, + + // 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 + ]; + + this.gl.bufferData(this.gl.ARRAY_BUFFER, new Float32Array(vertexNormals), + this.gl.STATIC_DRAW); + } +} + +export { Cube }; diff --git a/src/main.js b/src/main.js new file mode 100644 index 0000000..02463a8 --- /dev/null +++ b/src/main.js @@ -0,0 +1,240 @@ +import { mat4 } from 'gl-matrix'; + +import { Cube } from './cube.js'; +import { Plane } from './plane.js'; + +main(); + +// +// Start here +// +function main() { + const canvas = document.querySelector('#glcanvas'); + const gl = canvas.getContext('webgl'); + + /* + var spector = new SPECTOR.Spector(); + spector.displayUI(); + spector.spyCanvases(); + */ + + // If we don't have a GL context, give up now + if (!gl) { + alert('Unable to initialize WebGL. Your browser or machine may not support it.'); + return; + } + + // Vertex shader program + + const vsSource = ` + attribute vec4 aVertexPosition; + attribute vec3 aVertexNormal; + + uniform mat4 uModelMatrix; + uniform mat4 uViewMatrix; + uniform mat4 uProjectionMatrix; + + varying highp vec3 vFragNormal; + + void main(void) { + gl_Position = uProjectionMatrix * uViewMatrix * uModelMatrix * aVertexPosition; + + // assume affine transformation + vFragNormal = mat3(uModelMatrix) * aVertexNormal; + } + `; + + // Fragment shader program + + const fsSource = ` + varying highp vec3 vFragNormal; + + uniform sampler2D uSampler; + + void main(void) { + // Inputs + highp vec3 normal = normalize(vFragNormal); + + // Light/Material properties + highp vec3 lightDir = vec3(0.0, 1.0, 0.0); + highp vec3 materialDiffuse = vec3(0.6, 0.1, 0.1); + + // Ambient + highp vec3 ambient = vec3(0.2, 0.2, 0.2) * materialDiffuse; + + // Diffuse + highp float k_d = max(dot(lightDir, normal), 0.0); + highp vec3 diffuse = k_d * materialDiffuse; + + // Combine light contributions + highp vec3 color = ambient + diffuse; + gl_FragColor = vec4(color.rgb, 1.0); + } + `; + + let cube = new Cube(gl); + let plane = new Plane(gl); + + // Initialize a shader program; this is where all the lighting + // for the vertices and so forth is established. + const shaderProgram = initShaderProgram(gl, vsSource, fsSource); + + // Collect all the info needed to use the shader program. + // Look up which attributes our shader program is using + // for aVertexPosition, aVertexNormal, + // and look up uniform locations. + const programInfo = { + program: shaderProgram, + attribLocations: { + vertexPosition: gl.getAttribLocation(shaderProgram, 'aVertexPosition'), + vertexNormal: gl.getAttribLocation(shaderProgram, 'aVertexNormal'), + }, + uniformLocations: { + projectionMatrix: gl.getUniformLocation(shaderProgram, 'uProjectionMatrix'), + modelMatrix: gl.getUniformLocation(shaderProgram, 'uModelMatrix'), + viewMatrix: gl.getUniformLocation(shaderProgram, 'uViewMatrix'), + uSampler: gl.getUniformLocation(shaderProgram, 'uSampler'), + }, + }; + + var then = 0; + + // Draw the scene repeatedly + function render(now) { + now *= 0.001; // convert to seconds + const deltaTime = now - then; + then = now; + + cube.update(deltaTime); + + drawScene(gl, programInfo, cube, plane); + + requestAnimationFrame(render); + } + requestAnimationFrame(render); +} + +function initCameraMatrices(gl) { + // Create a perspective matrix, a special matrix that is + // used to simulate the distortion of perspective in a camera. + // Our field of view is 45 degrees, with a width/height + // ratio that matches the display size of the canvas + // and we only want to see objects between 0.1 units + // and 100 units away from the camera. + + const fieldOfView = 45 * Math.PI / 180; // in radians + const aspect = gl.canvas.clientWidth / gl.canvas.clientHeight; + const zNear = 0.1; + const zFar = 100.0; + const projectionMatrix = mat4.create(); + + // note: glmatrix.js always has the first argument + // as the destination to receive the result. + mat4.perspective(projectionMatrix, + fieldOfView, + aspect, + zNear, + zFar); + + // Set the drawing position to the "identity" point, which is + // the center of the scene. + const viewMatrix = mat4.create(); + + mat4.rotate(viewMatrix, + viewMatrix, + 0.5, + [1.0, 0.0, 0.0]); + + // Now move the drawing position a bit to where we want to + // start drawing the square. + mat4.translate(viewMatrix, // destination matrix + viewMatrix, // matrix to translate + [0.0, -6.0, -12.0]); // amount to translate + + return { + projection: projectionMatrix, + view: viewMatrix + }; +} + +// +// Draw the scene. +// +function drawScene(gl, programInfo, cube, plane) { + gl.clearColor(0.0, 0.0, 0.0, 1.0); // Clear to black, fully opaque + gl.clearDepth(1.0); // Clear everything + gl.enable(gl.DEPTH_TEST); // Enable depth testing + gl.depthFunc(gl.LEQUAL); // Near things obscure far things + + // Clear the canvas before we start drawing on it. + + gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); + + let cameraMatrices = initCameraMatrices(gl); + + // Tell WebGL to use our program when drawing + gl.useProgram(programInfo.program); + + // Set the shader uniforms + gl.uniformMatrix4fv( + programInfo.uniformLocations.projectionMatrix, + false, + cameraMatrices.projection); + gl.uniformMatrix4fv( + programInfo.uniformLocations.viewMatrix, + false, + cameraMatrices.view); + + cube.draw(programInfo); + plane.draw(programInfo); +} + +// +// Initialize a shader program, so WebGL knows how to draw our data +// +function initShaderProgram(gl, vsSource, fsSource) { + const vertexShader = loadShader(gl, gl.VERTEX_SHADER, vsSource); + const fragmentShader = loadShader(gl, gl.FRAGMENT_SHADER, fsSource); + + // Create the shader program + + const shaderProgram = gl.createProgram(); + gl.attachShader(shaderProgram, vertexShader); + gl.attachShader(shaderProgram, fragmentShader); + gl.linkProgram(shaderProgram); + + // If creating the shader program failed, alert + + if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { + alert('Unable to initialize the shader program: ' + gl.getProgramInfoLog(shaderProgram)); + return null; + } + + return shaderProgram; +} + +// +// creates a shader of the given type, uploads the source and +// compiles it. +// +function loadShader(gl, type, source) { + const shader = gl.createShader(type); + + // Send the source to the shader object + + gl.shaderSource(shader, source); + + // Compile the shader program + + gl.compileShader(shader); + + // See if it compiled successfully + + if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) { + alert('An error occurred compiling the shaders: ' + gl.getShaderInfoLog(shader)); + gl.deleteShader(shader); + return null; + } + + return shader; +} diff --git a/src/plane.js b/src/plane.js new file mode 100644 index 0000000..f12bd93 --- /dev/null +++ b/src/plane.js @@ -0,0 +1,116 @@ +import { mat4 } from 'gl-matrix'; + +class Plane +{ + constructor(gl) { + this.gl = gl; + this.createPositionBuffer(); + this.createIndexBuffer(); + this.createNormalBuffer(); + this.modelMatrix = mat4.create(); + } + + draw(programInfo) { + this.gl.uniformMatrix4fv( + programInfo.uniformLocations.modelMatrix, + false, + this.modelMatrix); + + // Tell WebGL how to pull out the positions from the position + // buffer into the vertexPosition attribute + { + const numComponents = 3; + const type = this.gl.FLOAT; + const normalize = false; + const stride = 0; + const offset = 0; + this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.positionBuffer); + this.gl.vertexAttribPointer( + programInfo.attribLocations.vertexPosition, + numComponents, + type, + normalize, + stride, + offset); + this.gl.enableVertexAttribArray( + programInfo.attribLocations.vertexPosition); + } + + // Tell WebGL how to pull out the normals from + // the normal buffer into the vertexNormal attribute. + { + const numComponents = 3; + const type = this.gl.FLOAT; + const normalize = false; + const stride = 0; + const offset = 0; + this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.normalBuffer); + this.gl.vertexAttribPointer( + programInfo.attribLocations.vertexNormal, + numComponents, + type, + normalize, + stride, + offset); + this.gl.enableVertexAttribArray( + programInfo.attribLocations.vertexNormal); + } + + // Tell WebGL which indices to use to index the vertices + this.gl.bindBuffer(this.gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer); + + { + const type = this.gl.UNSIGNED_SHORT; + const offset = 0; + this.gl.drawElements(this.gl.TRIANGLES, this.nbIndices, type, offset); + } + } + + createPositionBuffer() { + this.positionBuffer = this.gl.createBuffer(); + + this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.positionBuffer); + + const positions = [ + -10.0, -2.0, -10.0, + 10.0, -2.0, -10.0, + -10.0, -2.0, 10.0, + 10.0, -2.0, 10.0, + ]; + + this.gl.bufferData(this.gl.ARRAY_BUFFER, new Float32Array(positions), this.gl.STATIC_DRAW); + } + + createIndexBuffer() { + this.indexBuffer = this.gl.createBuffer(); + + this.gl.bindBuffer(this.gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer); + + const indices = [ + 2, 1, 0, + 3, 1, 2 + ]; + this.nbIndices = indices.length; + + this.gl.bufferData(this.gl.ELEMENT_ARRAY_BUFFER, + new Uint16Array(indices), this.gl.STATIC_DRAW); + } + + createNormalBuffer() { + this.normalBuffer = this.gl.createBuffer(); + + this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.normalBuffer); + + const vertexNormals = [ + 0.0, 1.0, 0.0, + 0.0, 1.0, 0.0, + 0.0, 1.0, 0.0, + 0.0, 1.0, 0.0, + ]; + + this.gl.bufferData(this.gl.ARRAY_BUFFER, new Float32Array(vertexNormals), + this.gl.STATIC_DRAW); + } +} + +export { Plane }; diff --git a/webgl.css b/webgl.css new file mode 100644 index 0000000..d03ac0b --- /dev/null +++ b/webgl.css @@ -0,0 +1,4 @@ +canvas { + border: 2px solid black; + background-color: black; +} \ No newline at end of file