Created
April 25, 2016 16:49
-
-
Save frondeus/03837d4cc64a5f6c1a416db0249b8442 to your computer and use it in GitHub Desktop.
WebGL tutorial
This file contains 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
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); | |
} |
This file contains 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
<!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> |
This file contains 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
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