<meta charset="utf-8"><title>WebGL test</title>
<script src="gl-matrix_2.2.1.js?app=data"></script>
<script id="simple-vs" type="x-shader/x-vertex">
attribute vec4 aVertColor;
gl_Position = uPMatrix * uMVMatrix * vec4(aVertPos, 1.0);
<script id="simple-fs" type="x-shader/x-fragment">
var gl, program, meshes = [], tStart, tLast, stopped,
camera = {}, mvMat = [mat4.create()], pMat = mat4.create(),
el = {}, x = {}, pi180 = Math.PI/180, eps = 1e-4,
key = {down:{}, on:{}, bind:{}};
var gl = document.getElementById('c');
gl = gl.getContext('webgl') || gl.getContext('experimental-webgl');
if (!gl) throw Error('Failed to initialize WebGL!');
gl.canvas.width = w || 640;
gl.canvas.height = h || 480;
gl.clearColor(0, 0, 0, 1);
gl.enable(gl.DEPTH_TEST);
function makeShaderProgram(name) {
var fragmentShader, vertexShader, shaderProgram;
function getShader (id) {
var shader, e = document.getElementById(id);
if (e.type === 'x-shader/x-fragment')
shader = gl.createShader(gl.FRAGMENT_SHADER);
else if (e.type === 'x-shader/x-vertex')
shader = gl.createShader(gl.VERTEX_SHADER);
else throw 'Unknown shader type "' + e.type + '"!';
gl.shaderSource(shader, e.textContent);
gl.compileShader(shader);
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS))
throw 'Failed to compile shader!';
fragmentShader = getShader(name + '-fs');
vertexShader = getShader(name + '-vs');
shaderProgram = gl.createProgram();
gl.attachShader(shaderProgram, fragmentShader);
gl.attachShader(shaderProgram, vertexShader);
gl.linkProgram(shaderProgram);
if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS))
throw 'Failed to link shader program "' + name + '"!';
function setProgram(prog) {
prog.vPosAtt = gl.getAttribLocation(prog, 'aVertPos');
gl.enableVertexAttribArray(prog.vPosAtt);
prog.vColAtt = gl.getAttribLocation(prog, 'aVertColor');
gl.enableVertexAttribArray(prog.vColAtt);
prog.mvMatUni = gl.getUniformLocation(prog, 'uMVMatrix');
prog.pMatUni = gl.getUniformLocation(prog, 'uPMatrix');
mvMat.unshift(mat4.clone(mvMat[0]));
if (mvMat.length <= 1) throw 'Invalid mvMatPop()!';
if (!d.vPos || d.vPos.length%3 !== 0)
throw 'Missing or invalid vPos';
d.vNum = d.vPos.length / 3;
if (!d.vCol) d.vCol = [1, 1, 1];
if (d.vCol.length === 3) while (d.vCol.length < d.vNum*3)
d.vCol = d.vCol.concat(d.vCol.slice(0, 3));
if (d.vCol.length === d.vPos.length)
while (d.vCol.length < d.vNum*4)
d.vCol.splice((d.vCol.length-d.vPos.length)*4+3,0,1);
if (d.vCol.length%4 !==0) throw 'Invalid vCol';
d.vPosBuf = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, d.vPosBuf);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(d.vPos), gl.STATIC_DRAW);
d.vColBuf = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, d.vColBuf);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(d.vCol), gl.STATIC_DRAW);
d.vIndBuf = gl.createBuffer();
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, d.vIndBuf);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(d.vInd), gl.STATIC_DRAW);
if (d.pos === undefined) d.pos = [0,0,0];
if (d.rot === undefined) d.rot = [0,0,0];
if (d.v === undefined) d.v = [0,0,0];
if (d.vRot === undefined) d.vRot = [0,0,0];
if (d.center === undefined) d.center = [0,0,0];
addMesh({name: 'triangle',
vPos: [-1,-1,-1, 0,1,0, -1,-1,1, 1,-1,1,
-1,-1,-1, 1,-1,-1, 0,1,0, 1,-1,1],
vCol: [0,0,1, 1,0,0, 0,1,0, 0,0,1,
0,0,1, 0,1,0, 1,0,0, 0,0,1],
vPos: [-1,-1,-1, -1,-1, 1,
m.v[0] = Math.abs(m.v[0]) < eps ? 0 : m.v[0] * .95;
m.v[1] = Math.abs(m.v[1]) < eps ? 0 : m.v[1] * .95;
m.v[2] = Math.abs(m.v[2]) < eps ? 0 : m.v[2] * .95;
m.vRot[0] = Math.abs(m.vRot[0]) < eps ? 0 : m.vRot[0] * .95;
m.vRot[1] = Math.abs(m.vRot[1]) < eps ? 0 : m.vRot[1] * .95;
m.vRot[2] = Math.abs(m.vRot[2]) < eps ? 0 : m.vRot[2] * .95;
mat4.translate(mvMat[0], mvMat[0], m.pos);
mat4.translate(mvMat[0], mvMat[0], [-m.center[0],-m.center[1],-m.center[2]]);
mat4.rotate(mvMat[0], mvMat[0], m.rot[0] * pi180, [1, 0, 0]);
mat4.rotate(mvMat[0], mvMat[0], m.rot[1] * pi180, [0, 1, 0]);
mat4.rotate(mvMat[0], mvMat[0], m.rot[2] * pi180, [0, 0, 1]);
mat4.translate(mvMat[0], mvMat[0], m.center);
gl.uniformMatrix4fv(program.mvMatUni, false, mvMat[0]);
gl.uniformMatrix4fv(program.pMatUni, false, pMat);
gl.bindBuffer(gl.ARRAY_BUFFER, m.vPosBuf);
gl.vertexAttribPointer(program.vPosAtt, 3, gl.FLOAT, false, 0, 0);
gl.bindBuffer(gl.ARRAY_BUFFER, m.vColBuf);
gl.vertexAttribPointer(program.vColAtt, 4, gl.FLOAT, false, 0, 0);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, m.vIndBuf);
gl.drawElements(gl.TRIANGLES, m.vInd.length, gl.UNSIGNED_SHORT, 0);
gl.drawArrays(gl.TRIANGLE_STRIP, 0, m.vNum);
mat4.perspective(pMat, camera.fov, gl.canvas.width/gl.canvas.height, camera.near, camera.far);
gl.clear(gl.COLOR_BUFFER_BIT|gl.DEPTH_BUFFER_BIT);
mat4.translate(mvMat[0], mvMat[0], camera.pos);
mat4.rotate(mvMat[0], mvMat[0], camera.rot[0] * pi180, [1, 0, 0]);
mat4.rotate(mvMat[0], mvMat[0], camera.rot[1] * pi180, [0, 1, 0]);
mat4.rotate(mvMat[0], mvMat[0], camera.rot[2] * pi180, [0, 0, 1]);
for (i=0, l=meshes.length; i<l; i++) drawMesh(meshes[i]);
for (i=0, l=meshes.length; i<l; i++) {
m.rot[0] = (m.rot[0] += t * m.vRot[0]) % 360;
m.rot[1] = (m.rot[1] += t * m.vRot[1]) % 360;
m.rot[2] = (m.rot[2] += t * m.vRot[2]) % 360;
if (m.x) for (j=0; j<m.x.length; j++)
if (typeof m.x[j] === 'function') m.x[j](m);
if (key.down[33]) x.curMesh = (x.curMesh + 1) % meshes.length;
if (key.down[34]) x.curMesh = (x.curMesh + meshes.length - 1) % meshes.length;
if (key.down[37]) meshes[x.curMesh].vRot[1] -= .01;
if (key.down[38]) meshes[x.curMesh].vRot[0] -= .01;
if (key.down[39]) meshes[x.curMesh].vRot[1] += .01;
if (key.down[40]) meshes[x.curMesh].vRot[0] += .01;
if (tStart === undefined) tStart = t;
for (i in key.down) if (key.down[i]) infoA.push(i);
el.stats.textContent = 'keys:['+infoA.join(',')+'] curMesh:'+x.curMesh+' vRot:['+meshes[x.curMesh].vRot.join(',')+']';
if (!stopped) requestAnimationFrame(tick);
el.stats = document.getElementById('stats');
el.info = document.getElementById('info');
el.log = document.getElementById('log');
setProgram(makeShaderProgram('simple'));
document.addEventListener('keydown', function(e){key.down[e.keyCode]=true;});
document.addEventListener('keyup', function(e){key.down[e.keyCode]=false;});
requestAnimationFrame(tick);
window.addEventListener('load', main);