Skip to content

Instantly share code, notes, and snippets.

@jgroszko
Created August 23, 2013 02:01
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jgroszko/6314805 to your computer and use it in GitHub Desktop.
Save jgroszko/6314805 to your computer and use it in GitHub Desktop.
A Simple WebGL Framework
/********/
/* Cube */
/********/
var Cube = function(gl, shaderProgram) {
this.gl = gl;
this.shaderProgram = shaderProgram;
var vertexData = [
-1.0, -1.0, 1.0, 0.0, 0.0, 1.0,
1.0, -1.0, 1.0, 0.0, 0.0, 1.0,
1.0, 1.0, 1.0, 0.0, 0.0, 1.0,
-1.0, 1.0, 1.0, 0.0, 0.0, 1.0,
-1.0, -1.0, -1.0, 0.0, 0.0, -1.0,
-1.0, 1.0, -1.0, 0.0, 0.0, -1.0,
1.0, 1.0, -1.0, 0.0, 0.0, -1.0,
1.0, -1.0, -1.0, 0.0, 0.0, -1.0,
-1.0, 1.0, -1.0, 0.0, 1.0, 0.0,
-1.0, 1.0, 1.0, 0.0, 1.0, 0.0,
1.0, 1.0, 1.0, 0.0, 1.0, 0.0,
1.0, 1.0, -1.0, 0.0, 1.0, 0.0,
-1.0, -1.0, -1.0, 0.0, -1.0, 0.0,
1.0, -1.0, -1.0, 0.0, -1.0, 0.0,
1.0, -1.0, 1.0, 0.0, -1.0, 0.0,
-1.0, -1.0, 1.0, 0.0, -1.0, 0.0,
1.0, -1.0, -1.0, -1.0, 0.0, 0.0,
1.0, 1.0, -1.0, -1.0, 0.0, 0.0,
1.0, 1.0, 1.0, -1.0, 0.0, 0.0,
1.0, -1.0, 1.0, -1.0, 0.0, 0.0,
-1.0, 1.0, -1.0, 1.0, 0.0, 0.0,
-1.0, 1.0, 1.0, 1.0, 0.0, 0.0,
-1.0, -1.0, 1.0, 1.0, 0.0, 0.0,
-1.0, -1.0, -1.0, 1.0, 0.0, 0.0
];
this.vertexCount = 24;
this.vertexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertexData), gl.STATIC_DRAW);
var indexData = [
0, 1, 2, 0, 2, 3,
4, 5, 6, 4, 6, 7,
8, 9, 10, 8, 10, 11,
12, 13, 14, 12, 14, 15,
16, 17, 18, 16, 18, 19,
20, 21, 22, 20, 22, 23
];
this.indexCount = 36;
this.indexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(indexData), gl.STATIC_DRAW);
shaderProgram.uniformValues.uColor = [0.8, 0.8, 0.8, 1.0];
};
Cube.prototype.draw = function() {
var gl = this.gl;
gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer);
gl.vertexAttribPointer(this.shaderProgram.getAttribLocation("aVertexPosition"),
3, gl.FLOAT, false, Float32Array.BYTES_PER_ELEMENT * 6,
0);
gl.vertexAttribPointer(this.shaderProgram.getAttribLocation("aNormal"),
3, gl.FLOAT, false, Float32Array.BYTES_PER_ELEMENT * 6,
Float32Array.BYTES_PER_ELEMENT * 3);
this.shaderProgram.apply();
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer);
gl.drawElements(gl.TRIANGLES, this.indexCount, gl.UNSIGNED_SHORT, 0);
};
/**********/
/* Camera */
/**********/
var Camera = function(gl, canvas) {
this.gl = gl;
this.canvas = canvas;
this.viewMatrix = mat4.lookAt([5.0, 5.0, 5.0],
[0.0, 0.0, 0.0],
[0.0, 1.0, 0.0]);
this.perspectiveMatrix = mat4.perspective(45,
canvas.width / canvas.height,
0.1, 100.0);
this.pvMatrix = mat4.multiply(this.perspectiveMatrix, this.viewMatrix, mat4.create());
// So we're not allocating a new matrix on every apply()
this.nMatrix = mat4.create();
};
Camera.prototype.apply = function(shader, mMatrix) {
shader.uniformValues.uPVMatrix = this.pvMatrix;
shader.uniformValues.uMMatrix = mMatrix;
shader.uniformValues.uNMatrix = mat4.transpose(mat4.inverse(mMatrix, this.nMatrix));
};
/******************/
/* Shader Program */
/******************/
var ShaderProgram = function(gl, vertexShaderId, fragmentShaderId) {
var fragmentShader = this._getShader(gl, fragmentShaderId),
vertexShader = this._getShader(gl, vertexShaderId),
shaderProgram = gl.createProgram();
gl.attachShader(shaderProgram, vertexShader);
gl.attachShader(shaderProgram, fragmentShader);
gl.linkProgram(shaderProgram);
if(!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) {
alert(gl.getShaderInfoLog(shaderProgram));
return null;
}
gl.useProgram(shaderProgram);
this.uniformLocations = {};
this.uniformValues = {};
this.attribLocations = {};
this.gl = gl;
this.shaderProgram = shaderProgram;
};
ShaderProgram.prototype.getAttribLocation = function(name) {
if(!(name in this.attribLocations)) {
this.attribLocations[name] = this.gl.getAttribLocation(this.shaderProgram, name);
if(this.attribLocations[name] === -1) {
console.log("Unable to find attribute " + name);
}
}
return this.attribLocations[name];
};
ShaderProgram.prototype.enableAttributes = function() {
for(var attrname in this.attribLocations) {
if(this.attribLocations[attrname] >= 0) {
this.gl.enableVertexAttribArray(this.attribLocations[attrname]);
}
}
};
ShaderProgram.prototype.getUniformLocation = function(name) {
if(!(name in this.uniformLocations)) {
var loc = this.gl.getUniformLocation(this.shaderProgram, name);
if(loc === null) {
console.log("Unable to find uniform " + name);
return null;
}
this.uniformLocations[name] = loc;
}
return this.uniformLocations[name];
};
ShaderProgram.prototype.applyUniforms = function() {
var gl = this.gl;
for(var name in this.uniformValues) {
var location = this.getUniformLocation(name);
if(location === null) {
console.log("Skipping " + name);
continue;
}
if(typeof this.uniformValues[name] === "boolean" ||
typeof this.uniformValues[name] === "number") {
gl.uniform1i(location, this.uniformValues[name]);
}
else if(this.uniformValues[name].length >= 16) {
gl.uniformMatrix4fv(location, false, this.uniformValues[name]);
}
else if(this.uniformValues[name].length == 4) {
gl.uniform4fv(location, this.uniformValues[name]);
}
else if(this.uniformValues[name].length == 3) {
gl.uniform3fv(location, this.uniformValues[name]);
}
}
};
ShaderProgram.prototype.apply = function() {
this.gl.useProgram(this.shaderProgram);
this.applyUniforms();
this.enableAttributes();
};
ShaderProgram.prototype._getShader = function(gl, id) {
var elem = document.getElementById(id);
var source = "";
var k = elem.firstChild;
while(k) {
if(k.nodeType == 3) {
source += k.textContent;
}
k = k.nextSibling;
}
var shader;
if(elem.type == 'x-shader/x-fragment') {
shader = gl.createShader(gl.FRAGMENT_SHADER);
}
else if(elem.type == 'x-shader/x-vertex') {
shader = gl.createShader(gl.VERTEX_SHADER);
}
else {
return null;
}
gl.shaderSource(shader, source);
gl.compileShader(shader);
if(!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
alert(gl.getShaderInfoLog(shader));
return null;
}
return shader;
};
/*************************/
/* requestAnimationFrame */
/*************************/
// shim layer with setTimeout fallback
// http://www.paulirish.com/2011/requestanimationframe-for-smart-animating/
window.requestAnimFrame = (function(){
return window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
function( callback ){
window.setTimeout(callback, 1000 / 60);
};
})();
/***************/
/* Application */
/***************/
var Application = function(){
this.canvas = document.querySelector('canvas');
this.gl = this.canvas.getContext("webgl");
this.shaderProgram = new ShaderProgram(this.gl, "vertex_shader", "fragment_shader");
this.camera = new Camera(this.gl, this.canvas);
this.cubeMMatrix = mat4.identity(mat4.create());
this.cube = new Cube(this.gl, this.shaderProgram);
this.viewportWidth = this.canvas.width;
this.viewportHeight = this.canvas.height;
this.gl.clearColor(0.0, 0.0, 0.4, 1.0);
this.gl.enable(this.gl.DEPTH_TEST);
this.shaderProgram.uniformValues.uAmbientColor = [0.3, 0.3, 0.3];
this.shaderProgram.uniformValues.uDirectionalColor = [0.3, 0.3, 0.3];
this.shaderProgram.uniformValues.uLightingDirection = [0.25, 0.75, 0.25];
this.draw();
};
Application.prototype.draw = function() {
var _this = this;
window.requestAnimFrame(function() {
_this.draw();
});
this.gl.viewport(0, 0, this.viewportWidth, this.viewportHeight);
this.gl.clear(this.gl.COLOR_BUFFER_BIT | this.gl.DEPTH_BUFFER_BIT);
this.camera.apply(this.shaderProgram, this.cubeMMatrix);
this.shaderProgram.apply(this.shaderProgram, this.cubeMMatrix);
this.cube.draw();
};
var app = new Application();
<!DOCTYPE html>
<html>
<head>
<title>Simple Framework</title>
</head>
<body>
<script type="x-shader/x-fragment" id="fragment_shader">
precision highp float;
uniform vec4 uColor;
varying vec3 vLightWeighting;
void main(void) {
gl_FragColor = vec4(uColor.rgb * vLightWeighting, uColor.a);
}
</script>
<script type="x-shader/x-vertex" id="vertex_shader">
attribute vec3 aNormal;
attribute vec3 aVertexPosition;
uniform mat4 uPVMatrix;
uniform mat4 uMMatrix;
uniform mat4 uNMatrix;
uniform vec3 uAmbientColor;
uniform vec3 uLightingDirection;
uniform vec3 uDirectionalColor;
varying vec3 vLightWeighting;
void main(void) {
gl_Position = uPVMatrix * uMMatrix * vec4(aVertexPosition, 1.0);
vec4 transformedNormal = uNMatrix * vec4(aNormal, 1.0);
float directionalLightWeighting = max(dot(transformedNormal.xyz, uLightingDirection), 0.0);
vLightWeighting = uAmbientColor + (uDirectionalColor * directionalLightWeighting);
}
</script>
<canvas width="640" height="480"></canvas>
<script src="//glmatrix.googlecode.com/files/glMatrix-0.9.5.min.js"></script>
<script src="Application.js"></script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment