Skip to content

Instantly share code, notes, and snippets.

@frondeus
Created April 25, 2016 16:49
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 frondeus/03837d4cc64a5f6c1a416db0249b8442 to your computer and use it in GitHub Desktop.
Save frondeus/03837d4cc64a5f6c1a416db0249b8442 to your computer and use it in GitHub Desktop.
WebGL tutorial
Game = {
initGl: function() {
var canvas = document.getElementById("glCanvas");
this.width = canvas.width;
this.height = canvas.height;
this.gl = null;
try {
this.gl = canvas.getContext("webgl") || canvas.getContext("experimental-webgl");
}
catch(e) {}
if(!this.gl) {
this.gl = null;
alert("Couldn't init WebGL");
}
canvas.onmousemove = handleMouse;
this.mouse = vec2.create();
this.gl.clearColor(0.0, 0.0, 0.3, 1.0);
this.gl.enable(this.gl.DEPTH_TEST);
this.gl.depthFunc(this.gl.LEQUAL);
},
getShader: function(id) {
var script = document.getElementById(id);
if(!script) return null;
var source = "";
var child = script.firstChild;
while(child) {
if(child.nodeType == child.TEXT_NODE)
source += child.textContent;
child = child.nextSibling;
}
var shader = null;
if(script.type == "x-shader/x-vertex") {
shader = this.gl.createShader(this.gl.VERTEX_SHADER);
}
else if(script.type == "x-shader/x-fragment") {
shader = this.gl.createShader(this.gl.FRAGMENT_SHADER);
}
else return null;
this.gl.shaderSource(shader, source);
this.gl.compileShader(shader);
if(!this.gl.getShaderParameter(shader, this.gl.COMPILE_STATUS)) {
console.log("Couldn't compile shader", this.gl.getShaderInfoLog(shader));
return null;
}
return shader;
},
loadShaders: function(vertexName, fragmentName) {
var vertex = this.getShader(vertexName);
var fragment = this.getShader(fragmentName);
var program = this.gl.createProgram();
this.gl.attachShader(program, vertex);
this.gl.attachShader(program, fragment);
this.gl.linkProgram(program);
if(!this.gl.getProgramParameter(program, this.gl.LINK_STATUS)) {
return null;
}
return program;
},
setMaterial: function() {
this.shader = this.loadShaders("vertex", "fragment");
this.gl.useProgram(this.shader);
this.attributes = {
pos: this.gl.getAttribLocation(this.shader, "pos"),
uv: this.gl.getAttribLocation(this.shader, "uv")
};
this.uniforms = {
mvp: this.gl.getUniformLocation(this.shader, "mvp"),
frame: this.gl.getUniformLocation(this.shader, "frame"),
frameSize: this.gl.getUniformLocation(this.shader, "frameSize"),
texture: this.gl.getUniformLocation(this.shader, "texture")
};
this.gl.enableVertexAttribArray(this.attributes.pos);
this.gl.enableVertexAttribArray(this.attributes.uv);
},
loadQuad: function() {
var pos = [
1.0, 1.0,
-1.0, 1.0,
1.0, -1.0,
-1.0, -1.0,
];
var uv = [
1.0, 1.0,
0.0, 1.0,
1.0, 0.0,
0.0, 0.0
];
this.buffers = {};
this.buffers.pos = this.gl.createBuffer();
this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.buffers.pos);
this.gl.bufferData(this.gl.ARRAY_BUFFER, new Float32Array(pos), this.gl.STATIC_DRAW);
this.buffers.uv = this.gl.createBuffer();
this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.buffers.uv);
this.gl.bufferData(this.gl.ARRAY_BUFFER, new Float32Array(uv), this.gl.STATIC_DRAW);
this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.buffers.pos);
this.gl.vertexAttribPointer(this.attributes.pos, 2, this.gl.FLOAT, false, 0, 0);
this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.buffers.uv);
this.gl.vertexAttribPointer(this.attributes.uv, 2, this.gl.FLOAT, false, 0, 0);
},
loadTilset: function() {
this.textures = {
tileset: this.gl.createTexture()
};
this.tileSize = 0.25 * 0.5;
var image = new Image();
image.src = "tileset.png";
var that = this;
image.onload = function() {
that.gl.bindTexture(that.gl.TEXTURE_2D, that.textures.tileset);
that.gl.texImage2D(that.gl.TEXTURE_2D, 0, that.gl.RGBA, that.gl.RGBA, that.gl.UNSIGNED_BYTE, image);
that.gl.texParameteri(that.gl.TEXTURE_2D, that.gl.TEXTURE_MAG_FILTER, that.gl.NEAREST);
that.gl.texParameteri(that.gl.TEXTURE_2D, that.gl.TEXTURE_MIN_FILTER, that.gl.NEAREST);
that.gl.activeTexture(that.gl.TEXTURE0);
that.gl.bindTexture(that.gl.TEXTURE_2D, that.textures.tileset);
that.gl.uniform1i(that.uniforms.texture, 0);
};
},
initMatrices: function() {
this.Proj = mat4.create();
mat4.ortho(this.Proj, 0, this.width, this.height, 0, 0.01, 100.0);
}
};
function handleMouse() {
vec2.set(Game.mouse, event.clientX, event.clientY);
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>Space Shooter</title>
</head>
<body>
<canvas id="glCanvas" width="800" height="600"></canvas>
<script id="vertex" type="x-shader/x-vertex" charset="utf-8">
attribute vec2 pos; // x, y
attribute vec2 uv; // u, v
uniform mat4 mvp;
uniform vec2 frame;
uniform vec2 frameSize;
varying highp vec2 Uv; // Out -> Uv
void main(void) {
Uv = (frame + uv) * frameSize;
gl_Position = mvp * vec4(pos, 0.0, 1.0);
}
</script>
<script id="fragment" type="x-shader/x-fragment" charset="utf-8">
varying highp vec2 Uv;
uniform sampler2D texture;
void main(void) {
gl_FragColor = texture2D(texture, Uv);
if(gl_FragColor.a < 0.9) discard;
}
</script>
<script src="gl-matrix-min.js" type="text/javascript" charset="utf-8"></script>
<script src="framework.js" type="text/javascript" charset="utf-8"></script>
<script src="main.js" type="text/javascript" charset="utf-8"></script>
</body>
</html>
var Sprite = function(x, y) {
this.pos = vec2.create();
this.vel = vec2.create();
this.acc = vec2.create();
this.scale = vec2.create();
this.frame = vec2.create();
this.rot = 0.0;
vec2.set(this.pos, x, y);
vec2.set(this.scale, 16, 16);
vec2.set(this.frame, 0, 0);
};
Game.init = function() {
this.initGl();
this.setMaterial();
this.loadQuad();
this.loadTilset();
this.initMatrices();
this.sprites = [];
this.sprites.push(new Sprite(400, 300));
this.sprites.push(new Sprite(100, 500));
var asteroid = new Sprite(500, 100);
vec2.set(asteroid.frame, 2, 0);
this.sprites.push(asteroid);
};
Game.update = function(dt) {
var en = this.sprites[0];
vec2.set(en.frame, dt % 2, 0);
var d = vec2.create();
vec2.subtract(d, this.mouse, en.pos);
vec2.normalize(d, d);
vec2.set(en. acc, d[0] * 100, d[1] * 100);
for(var e in this.sprites) {
var sprite = this.sprites[e];
var a = vec2.clone(sprite.acc);
a[0] *= dt;
a[1] *= dt;
vec2.add(sprite.vel, sprite.vel, a);
var v = vec2.clone(sprite.vel);
v[0] *= dt;
v[1] *= dt;
vec2.add(sprite.pos, sprite.pos, v);
sprite.rot = Math.atan2(a[1], a[0]);
}
};
Game.render = function() {
this.gl.clear(this.gl.COLOR_BUFFER_BIT | this.gl.DEPTH_BUFFER_BIT);
for(var s in this.sprites) {
var sprite = this.sprites[s];
var matrix = {
model: mat4.create(),
mvp: mat4.create()
};
var trans = vec3.create();
vec3.set(trans, sprite.pos[0], sprite.pos[1], -1);
mat4.translate(matrix.model, matrix.model, trans);
var scale = vec3.create();
vec3.set(scale, sprite.scale[0], sprite.scale[1], 10);
mat4.scale(matrix.model, matrix.model, scale);
mat4.rotateZ(matrix.model, matrix.model, sprite.rot);
mat4.multiply(matrix.mvp, this.Proj, matrix.model);
this.gl.uniform2f(this.uniforms.frame, sprite.frame[0], sprite.frame[1]);
this.gl.uniform2f(this.uniforms.frameSize, this.tileSize, 1);
this.gl.uniformMatrix4fv(this.uniforms.mvp, false, matrix.mvp);
this.gl.drawArrays(this.gl.TRIANGLE_STRIP, 0, 4);
}
};
window.onload = function() {
Game.init();
var oldTime = Date.now();
function render() {
var newTime = Date.now();
var dt = (newTime - oldTime) / 1000;
oldTime = newTime;
Game.update(dt);
Game.render();
requestAnimationFrame(render);
}
render();
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment