A Pen by Saran Ahluwalia on CodePen.
Created
December 25, 2016 22:19
-
-
Save ahlusar1989/892f7de5ebc697088cc62c6bbb46ccf8 to your computer and use it in GitHub Desktop.
WebGL Man
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <canvas id="canvas"></canvas> | |
| <script src="http://webgl2fundamentals.org/webgl/resources/twgl-full.min.js"></script> | |
| <script src="http://webgl2fundamentals.org/webgl/resources/webgl-lessons-helper.js"></script> | |
| <script src="http://webgl2fundamentals.org/webgl/resources/3d-math.js"></script> | |
| <script src="http://webgl2fundamentals.org/webgl/resources/flattened-primitives.js"></script> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| "use strict"; | |
| var vs = `#version 300 es | |
| in vec4 a_position; | |
| in vec4 a_color; | |
| uniform mat4 u_matrix; | |
| out vec4 v_color; | |
| void main() { | |
| // Multiply the position by the matrix. | |
| gl_Position = u_matrix * a_position; | |
| // Pass the color to the fragment shader. | |
| v_color = a_color; | |
| } | |
| `; | |
| var fs = `#version 300 es | |
| precision mediump float; | |
| // Passed in from the vertex shader. | |
| in vec4 v_color; | |
| uniform vec4 u_colorMult; | |
| uniform vec4 u_colorOffset; | |
| out vec4 outColor; | |
| void main() { | |
| outColor = v_color * u_colorMult + u_colorOffset; | |
| } | |
| `; | |
| var TRS = function() { | |
| this.translation = [0, 0, 0]; | |
| this.rotation = [0, 0, 0]; | |
| this.scale = [1, 1, 1]; | |
| }; | |
| TRS.prototype.getMatrix = function(dst) { | |
| dst = dst || new Float32Array(16); | |
| var t = this.translation; | |
| var r = this.rotation; | |
| var s = this.scale; | |
| // compute a matrix from translation, rotation, and scale | |
| m4.translation(t[0], t[1], t[2], dst); | |
| m4.xRotate(dst, r[0], dst); | |
| m4.yRotate(dst, r[1], dst); | |
| m4.zRotate(dst, r[2], dst); | |
| m4.scale(dst, s[0], s[1], s[2], dst); | |
| return dst; | |
| }; | |
| var Node = function(source) { | |
| this.children = []; | |
| this.localMatrix = m4.identity(); | |
| this.worldMatrix = m4.identity(); | |
| this.source = source; | |
| }; | |
| Node.prototype.setParent = function(parent) { | |
| // remove us from our parent | |
| if (this.parent) { | |
| var ndx = this.parent.children.indexOf(this); | |
| if (ndx >= 0) { | |
| this.parent.children.splice(ndx, 1); | |
| } | |
| } | |
| // Add us to our new parent | |
| if (parent) { | |
| parent.children.push(this); | |
| } | |
| this.parent = parent; | |
| }; | |
| Node.prototype.updateWorldMatrix = function(matrix) { | |
| var source = this.source; | |
| if (source) { | |
| source.getMatrix(this.localMatrix); | |
| } | |
| if (matrix) { | |
| // a matrix was passed in so do the math | |
| m4.multiply(matrix, this.localMatrix, this.worldMatrix); | |
| } else { | |
| // no matrix was passed in so just copy. | |
| m4.copy(this.localMatrix, this.worldMatrix); | |
| } | |
| // now process all the children | |
| var worldMatrix = this.worldMatrix; | |
| this.children.forEach(function(child) { | |
| child.updateWorldMatrix(worldMatrix); | |
| }); | |
| }; | |
| function main() { | |
| // Get A WebGL context | |
| /** @type {HTMLCanvasElement} */ | |
| var canvas = document.getElementById("canvas"); | |
| var gl = canvas.getContext("webgl2"); | |
| if (!gl) { | |
| return; | |
| } | |
| // Tell the twgl to match position with a_position, n | |
| // normal with a_normal etc.. | |
| twgl.setAttributePrefix("a_"); | |
| var cubeBufferInfo = flattenedPrimitives.createCubeBufferInfo(gl, 1); | |
| // setup GLSL program | |
| var programInfo = twgl.createProgramInfo(gl, [vs, fs]); | |
| var cubeVAO = twgl.createVAOFromBufferInfo(gl, programInfo, cubeBufferInfo); | |
| function degToRad(d) { | |
| return d * Math.PI / 180; | |
| } | |
| var fieldOfViewRadians = degToRad(60); | |
| var objectsToDraw = []; | |
| var objects = []; | |
| var nodeInfosByName = {}; | |
| // Let's make all the nodes | |
| var blockGuyNodeDescriptions = | |
| { | |
| name: "point between feet", | |
| draw: false, | |
| children: [ | |
| { | |
| name: "waist", | |
| translation: [0, 3, 0], | |
| children: [ | |
| { | |
| name: "torso", | |
| translation: [0, 2, 0], | |
| children: [ | |
| { | |
| name: "neck", | |
| translation: [0, 1, 0], | |
| children: [ | |
| { | |
| name: "head", | |
| translation: [0, 1, 0], | |
| }, | |
| ], | |
| }, | |
| { | |
| name: "left-arm", | |
| translation: [-1, 0, 0], | |
| children: [ | |
| { | |
| name: "left-forearm", | |
| translation: [-1, 0, 0], | |
| children: [ | |
| { | |
| name: "left-hand", | |
| translation: [-1, 0, 0], | |
| }, | |
| ], | |
| }, | |
| ], | |
| }, | |
| { | |
| name: "right-arm", | |
| translation: [1, 0, 0], | |
| children: [ | |
| { | |
| name: "right-forearm", | |
| translation: [1, 0, 0], | |
| children: [ | |
| { | |
| name: "right-hand", | |
| translation: [1, 0, 0], | |
| }, | |
| ], | |
| }, | |
| ], | |
| }, | |
| ], | |
| }, | |
| { | |
| name: "left-leg", | |
| translation: [-1, -1, 0], | |
| children: [ | |
| { | |
| name: "left-calf", | |
| translation: [0, -1, 0], | |
| children: [ | |
| { | |
| name: "left-foot", | |
| translation: [0, -1, 0], | |
| }, | |
| ], | |
| }, | |
| ], | |
| }, | |
| { | |
| name: "right-leg", | |
| translation: [1, -1, 0], | |
| children: [ | |
| { | |
| name: "right-calf", | |
| translation: [0, -1, 0], | |
| children: [ | |
| { | |
| name: "right-foot", | |
| translation: [0, -1, 0], | |
| }, | |
| ], | |
| }, | |
| ], | |
| }, | |
| ], | |
| }, | |
| ], | |
| }; | |
| function makeNode(nodeDescription) { | |
| var trs = new TRS(); | |
| var node = new Node(trs); | |
| nodeInfosByName[nodeDescription.name] = { | |
| trs: trs, | |
| node: node, | |
| }; | |
| trs.translation = nodeDescription.translation || trs.translation; | |
| if (nodeDescription.draw !== false) { | |
| node.drawInfo = { | |
| uniforms: { | |
| u_colorOffset: [0, 0, 0.6, 0], | |
| u_colorMult: [0.6, 0.1, 0.1, 1], | |
| }, | |
| programInfo: programInfo, | |
| bufferInfo: cubeBufferInfo, | |
| vertexArray: cubeVAO, | |
| }; | |
| objectsToDraw.push(node.drawInfo); | |
| objects.push(node); | |
| } | |
| makeNodes(nodeDescription.children).forEach(function(child) { | |
| child.setParent(node); | |
| }); | |
| return node; | |
| } | |
| function makeNodes(nodeDescriptions) { | |
| return nodeDescriptions ? nodeDescriptions.map(makeNode) : []; | |
| } | |
| var scene = makeNode(blockGuyNodeDescriptions); | |
| requestAnimationFrame(drawScene); | |
| // Draw the scene. | |
| function drawScene(time) { | |
| time *= 0.001; | |
| twgl.resizeCanvasToDisplaySize(gl.canvas); | |
| // Tell WebGL how to convert from clip space to pixels | |
| gl.viewport(0, 0, gl.canvas.width, gl.canvas.height); | |
| gl.enable(gl.CULL_FACE); | |
| gl.enable(gl.DEPTH_TEST); | |
| // Compute the projection matrix | |
| var aspect = gl.canvas.clientWidth / gl.canvas.clientHeight; | |
| var projectionMatrix = | |
| m4.perspective(fieldOfViewRadians, aspect, 1, 200); | |
| // Compute the camera's matrix using look at. | |
| var cameraPosition = [4, 3.5, 10]; | |
| var target = [0, 3.5, 0]; | |
| var up = [0, 1, 0]; | |
| var cameraMatrix = m4.lookAt(cameraPosition, target, up); | |
| // Make a view matrix from the camera matrix. | |
| var viewMatrix = m4.inverse(cameraMatrix); | |
| var viewProjectionMatrix = m4.multiply(projectionMatrix, viewMatrix); | |
| var adjust; | |
| var speed = 3; | |
| var c = time * speed; | |
| adjust = Math.abs(Math.sin(c)); | |
| nodeInfosByName["point between feet"].trs.translation[1] = adjust; | |
| adjust = Math.sin(c); | |
| nodeInfosByName["left-leg" ].trs.rotation[0] = adjust; | |
| nodeInfosByName["right-leg"].trs.rotation[0] = -adjust; | |
| adjust = Math.sin(c + 0.1) * 0.4; | |
| nodeInfosByName["left-calf" ].trs.rotation[0] = -adjust; | |
| nodeInfosByName["right-calf"].trs.rotation[0] = adjust; | |
| adjust = Math.sin(c + 0.1) * 0.4; | |
| nodeInfosByName["left-foot" ].trs.rotation[0] = -adjust; | |
| nodeInfosByName["right-foot"].trs.rotation[0] = adjust; | |
| adjust = Math.sin(c) * 0.4; | |
| nodeInfosByName["left-arm" ].trs.rotation[2] = adjust; | |
| nodeInfosByName["right-arm"].trs.rotation[2] = adjust; | |
| adjust = Math.sin(c + 0.1) * 0.4; | |
| nodeInfosByName["left-forearm" ].trs.rotation[2] = adjust; | |
| nodeInfosByName["right-forearm"].trs.rotation[2] = adjust; | |
| adjust = Math.sin(c - 0.1) * 0.4; | |
| nodeInfosByName["left-hand" ].trs.rotation[2] = adjust; | |
| nodeInfosByName["right-hand"].trs.rotation[2] = adjust; | |
| adjust = Math.sin(c) * 0.4; | |
| nodeInfosByName["waist"].trs.rotation[1] = adjust; | |
| adjust = Math.sin(c) * 0.4; | |
| nodeInfosByName["torso"].trs.rotation[1] = adjust; | |
| adjust = Math.sin(c + 0.25) * 0.4; | |
| nodeInfosByName["neck"].trs.rotation[1] = adjust; | |
| adjust = Math.sin(c + 0.5) * 0.4; | |
| nodeInfosByName["head"].trs.rotation[1] = adjust; | |
| adjust = Math.cos(c * 2) * 0.4; | |
| nodeInfosByName["head"].trs.rotation[0] = adjust; | |
| // Update all world matrices in the scene graph | |
| scene.updateWorldMatrix(); | |
| // Compute all the matrices for rendering | |
| objects.forEach(function(object) { | |
| object.drawInfo.uniforms.u_matrix = m4.multiply(viewProjectionMatrix, object.worldMatrix); | |
| }); | |
| // ------ Draw the objects -------- | |
| twgl.drawObjectList(gl, objectsToDraw); | |
| requestAnimationFrame(drawScene); | |
| } | |
| } | |
| main(); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| html { | |
| box-sizing: border-box; | |
| } | |
| *, *:before, *:after { | |
| box-sizing: inherit; | |
| } | |
| body { | |
| background-color: #aaa; | |
| font-family: Sans-Serif; | |
| } | |
| canvas { | |
| background-color: #fff; | |
| border: 1px solid black; | |
| /* NOTE: This size is changed if in iframe - see below '.iframe canvas' */ | |
| width: 400px; | |
| height: 300px; | |
| display: block; | |
| } | |
| #uiContainer { | |
| position: absolute; | |
| top: 10px; | |
| right: 10px; | |
| z-index: 3; | |
| font-family: monospace; | |
| text-shadow: | |
| -1px -1px 0 #FFF, | |
| 1px -1px 0 #FFF, | |
| -1px 1px 0 #FFF, | |
| 1px 1px 0 #FFF; | |
| } | |
| #ui { | |
| opacity: 0.8; | |
| } | |
| .gman-slider-label { | |
| font-size: small; | |
| min-width: 10em; | |
| text-align: right; | |
| } | |
| .gman-slider-value { | |
| float: right; | |
| font-size: small; | |
| order: 1; | |
| min-width: 3em; | |
| } | |
| .gman-slider-upper { | |
| height: 1.5em; | |
| } | |
| .gman-slider-outer { | |
| float:right; | |
| display: flex; | |
| align-items: center; | |
| height: 1.7em; | |
| } | |
| .gman-slider-slider { | |
| opacity: 0.5; | |
| font-size: large; | |
| min-width: 120px; | |
| margin-left: .3em; | |
| margin-right: .3em; | |
| } | |
| /* styles to apply if in an iframe */ | |
| html.iframe { | |
| height: 100vh; | |
| } | |
| body.iframe { | |
| width: 100vw; | |
| height: 100vh; | |
| margin: 0; | |
| overflow: hidden; | |
| } | |
| .iframe>.description { | |
| display: none; | |
| } | |
| .iframe .divcavnas { | |
| width: 100vw; | |
| height: 100vh; | |
| } | |
| .iframe canvas { | |
| width: 100vw; | |
| height: 100vh; | |
| border: none; | |
| } | |
| .iframe>#example { | |
| width: 100%; | |
| height: 100%; | |
| } | |
| #ui #rotation>canvas { | |
| background-color: rgba(255, 255, 255, 0.6); | |
| } | |
| #ui { | |
| width: 200px; | |
| } | |
| .console { | |
| font-family: monospace; | |
| max-height: 50%; | |
| position: fixed; | |
| bottom: 0; | |
| left: 0; | |
| width: 100%; | |
| overflow: auto; | |
| background: #DDD; | |
| } | |
| .console>div { | |
| white-space: pre-wrap; | |
| border-top: 1px solid black; | |
| padding-left: 0.2em; | |
| padding-right: 0.2em; | |
| } | |
| .console>.error { | |
| background: red; | |
| color: white; | |
| } | |
| .contextlost { | |
| display: flex; | |
| justify-content: center; | |
| align-items: center; | |
| width: 100vw; | |
| height: 100vh; | |
| position: absolute; | |
| left: 0; | |
| top: 0; | |
| background-color: rgba(0, 0, 0, 0.8); | |
| color: white; | |
| z-index: 10000; | |
| } | |
| .contextlost>div { | |
| padding: 1em; | |
| border-radius: 0.5em; | |
| background: red; | |
| color: white; | |
| } | |
| @media (max-width: 390px) { | |
| pre { | |
| font-size: xx-small !important; | |
| max-width: 300px !important; | |
| } | |
| } | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment