Skip to content

Instantly share code, notes, and snippets.

@ahlusar1989
Created December 25, 2016 22:19
Show Gist options
  • Save ahlusar1989/892f7de5ebc697088cc62c6bbb46ccf8 to your computer and use it in GitHub Desktop.
Save ahlusar1989/892f7de5ebc697088cc62c6bbb46ccf8 to your computer and use it in GitHub Desktop.
WebGL Man
<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>
"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();
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