<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta name="viewport" content="width=device-width" />
<title>WebGL thing</title>
<style type="text/css">
* {
margin: 0; padding: 0; border: 0;
background: #000 url("https://farm9.staticflickr.com/8692/16140033894_1edcf94d33_o.png");
}
canvas { display: block; }
</style>
</head>
<body><canvas></canvas></body>
<script id="fragment" type="x-shader/fragment">
precision mediump float;
varying vec2 Texel;
uniform sampler2D sampler;
/* texture mapping happens here */
void main () {
// gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);
// gl_FragColor = vec4(Color, 1);
vec2 center = vec2(1.0 / 8.0, 1.0 / 8.0);
vec2 yflip = vec2(1.0, -1.0);
gl_FragColor = texture2D(sampler, Texel * yflip + center);
}
</script><script id="vertex" type="x-shader/vertex">
uniform mat4 projection;
attribute vec3 coord;
attribute vec2 texel;
varying vec2 Texel;
uniform vec3 mRotate, mTranslate;
uniform vec3 vRotate, vTranslate;
// model = scale * rotate * translate
uniform float mScale;
uniform float vScale;
uniform float invert;
mat4 rotation_matrix (vec3 rotate) {
return mat4( // around X axis
1.0, 0.0, 0.0, 0.0,
0.0, cos(rotate.x), -sin(rotate.x), 0.0,
0.0, sin(rotate.x), cos(rotate.x), 0.0,
0.0, 0.0, 0.0, 1.0
) * mat4( // around Y axis
cos(rotate.y), 0.0, sin(rotate.y), 0.0,
0.0, 1.0, 0.0, 0.0,
-sin(rotate.y), 0.0, cos(rotate.y), 0.0,
0.0, 0.0, 0.0, 1.0
) * mat4( // around Z axis
cos(rotate.z), sin(rotate.z), 0.0, 0.0,
-sin(rotate.z), cos(rotate.z), 0.0, 0.0,
0.0, 0.0, 1.0, 0.0,
0.0, 0.0, 0.0, 1.0
);
}
mat4 scale_matrix (float scale) {
return mat4(
scale, 0.0, 0.0, 0.0,
0.0, scale, 0.0, 0.0,
0.0, 0.0, scale, 0.0,
0.0, 0.0, 0.0, 1.0
);
}
/* OpenGL matrices appear to be flipped along the x-w diagonal.
1.0, 0.0, 0.0, 0.0, <-.
0.0, 1.0, 0.0, 0.0, <-+- projection
0.0, 0.0, 1.0, 0.0, <-'
0.0, 0.0, 0.0, 1.0
^ ^ ^
`----+----'
|
translate
*/
mat4 translation_matrix (vec3 translate) {
return mat4(
1.0, 0.0, 0.0, 0.0,
0.0, 1.0, 0.0, 0.0,
0.0, 0.0, 1.0, 0.0,
translate.x, translate.y, translate.z, 1.0
);
}
/* coordinate transformations happen here */
void main () {
//Color = invert - color * (invert * 2.0 - 1.0);
/* that line is the mathematical expression of this block
* of code, only without any branches, because GPU.
if (0.0 == invert) Color = vec3(1.0) - color;
else Color = color;
*/
Texel = texel;
//gl_PointSize = 1.1;
/*
mat4 view
= translation_matrix(vTranslate)
* scale_matrix(vScale)
* rotation_matrix(vRotate);
*/
mat4 view
= rotation_matrix(vRotate)
* translation_matrix(vTranslate)
* scale_matrix(vScale);
mat4 model
= translation_matrix(mTranslate)
* scale_matrix(mScale)
* rotation_matrix(mRotate);
gl_Position = projection * view * model * vec4(coord, 1.0);
}
</script><script type="text/javascript">
var canvas = document.getElementsByTagName("canvas")[0];
var gl = canvas.getContext("webgl") || canvas.getContext("experimental-webgl");
if (!gl) {
alert("sorry, you need webgl for this.");
//history.back();
}
/*
var texture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
*/
gl.enable(gl.DEPTH_TEST);
/*
gl.depthFunc(gl.LESS);
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
gl.enable(gl.BLEND);
*/
gl.lineWidth(2);
function compile_shader (type, id) {
var shader = gl.createShader(type);
gl.shaderSource(shader, document.getElementById(id).innerHTML);
gl.compileShader(shader);
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS))
console.log(gl.getShaderInfoLog(shader));
return shader;
}
var shader = gl.createProgram();
gl.attachShader(shader, compile_shader(gl.FRAGMENT_SHADER, "fragment"));
gl.attachShader(shader, compile_shader(gl.VERTEX_SHADER, "vertex"));
gl.linkProgram(shader);
if (!gl.getProgramParameter(shader, gl.LINK_STATUS))
console.log(gl.getProgramInfoLog(shader));
gl.useProgram(shader);
function load_buffer (data, type) {
var vbo = gl.createBuffer();
gl.bindBuffer(type, vbo);
gl.bufferData(type, data, gl.STATIC_DRAW);
return vbo;
}
function cached_uniform1f (pgm, dest) {
var u = gl.getUniformLocation(pgm, dest);
return function (v) { return gl.uniform1f(u, v); }
}
var cube = {
invert: cached_uniform1f(shader, "invert"),
attributes: {
coord: gl.getAttribLocation(shader, "coord"),
texel: gl.getAttribLocation(shader, "texel")
},
vertices: load_buffer(new Float32Array([
// x, y, z, s, t
-4/4,-4/4, 0, 0/4, 0/4,
-3/4,-4/4, 0, 1/4, 0/4,
-2/4,-4/4, 0, 2/4, 0/4,
-1/4,-4/4, 0, 3/4, 0/4,
0/4,-4/4, 0, 4/4, 0/4,
1/4,-4/4, 0, 5/4, 0/4,
2/4,-4/4, 0, 6/4, 0/4,
3/4,-4/4, 0, 7/4, 0/4,
4/4,-4/4, 0, 8/4, 0/4,
-4/4,-3/4, 0, 0/4, 1/4,
-3/4,-3/4, 0, 1/4, 1/4,
-2/4,-3/4, 0, 2/4, 1/4,
-1/4,-3/4, 0, 3/4, 1/4,
0/4,-3/4, 0, 4/4, 1/4,
1/4,-3/4, 0, 5/4, 1/4,
2/4,-3/4, 0, 6/4, 1/4,
3/4,-3/4, 0, 7/4, 1/4,
4/4,-3/4, 0, 8/4, 1/4,
-4/4,-2/4, 0, 0/4, 2/4,
-3/4,-2/4, 0, 1/4, 2/4,
-2/4,-2/4, 0, 2/4, 2/4,
-1/4,-2/4, 0, 3/4, 2/4,
0/4,-2/4, 0, 4/4, 2/4,
1/4,-2/4, 0, 5/4, 2/4,
2/4,-2/4, 0, 6/4, 2/4,
3/4,-2/4, 0, 7/4, 2/4,
4/4,-2/4, 0, 8/4, 2/4,
-4/4,-1/4, 0, 0/4, 3/4,
-3/4,-1/4, 0, 1/4, 3/4,
-2/4,-1/4, 0, 2/4, 3/4,
-1/4,-1/4, 0, 3/4, 3/4,
0/4,-1/4, 0, 4/4, 3/4,
1/4,-1/4, 0, 5/4, 3/4,
2/4,-1/4, 0, 6/4, 3/4,
3/4,-1/4, 0, 7/4, 3/4,
4/4,-1/4, 0, 8/4, 3/4,
-4/4, 0/4, 0, 0/4, 4/4,
-3/4, 0/4, 0, 1/4, 4/4,
-2/4, 0/4, 0, 2/4, 4/4,
-1/4, 0/4, 0, 3/4, 4/4,
0/4, 0/4, 0, 4/4, 4/4,
1/4, 0/4, 0, 5/4, 4/4,
2/4, 0/4, 0, 6/4, 4/4,
3/4, 0/4, 0, 7/4, 4/4,
4/4, 0/4, 0, 8/4, 4/4,
-4/4, 1/4, 0, 0/4, 5/4,
-3/4, 1/4, 0, 1/4, 5/4,
-2/4, 1/4, 0, 2/4, 5/4,
-1/4, 1/4, 0, 3/4, 5/4,
0/4, 1/4, 0, 4/4, 5/4,
1/4, 1/4, 0, 5/4, 5/4,
2/4, 1/4, 0, 6/4, 5/4,
3/4, 1/4, 0, 7/4, 5/4,
4/4, 1/4, 0, 8/4, 5/4,
-4/4, 2/4, 0, 0/4, 6/4,
-3/4, 2/4, 0, 1/4, 6/4,
-2/4, 2/4, 0, 2/4, 6/4,
-1/4, 2/4, 0, 3/4, 6/4,
0/4, 2/4, 0, 4/4, 6/4,
1/4, 2/4, 0, 5/4, 6/4,
2/4, 2/4, 0, 6/4, 6/4,
3/4, 2/4, 0, 7/4, 6/4,
4/4, 2/4, 0, 8/4, 6/4,
-4/4, 3/4, 0, 0/4, 7/4,
-3/4, 3/4, 0, 1/4, 7/4,
-2/4, 3/4, 0, 2/4, 7/4,
-1/4, 3/4, 0, 3/4, 7/4,
0/4, 3/4, 0, 4/4, 7/4,
1/4, 3/4, 0, 5/4, 7/4,
2/4, 3/4, 0, 6/4, 7/4,
3/4, 3/4, 0, 7/4, 7/4,
4/4, 3/4, 0, 8/4, 7/4,
-4/4, 4/4, 0, 0/4, 8/4,
-3/4, 4/4, 0, 1/4, 8/4,
-2/4, 4/4, 0, 2/4, 8/4,
-1/4, 4/4, 0, 3/4, 8/4,
0/4, 4/4, 0, 4/4, 8/4,
1/4, 4/4, 0, 5/4, 8/4,
2/4, 4/4, 0, 6/4, 8/4,
3/4, 4/4, 0, 7/4, 8/4,
4/4, 4/4, 0, 8/4, 8/4
]), gl.ARRAY_BUFFER),
elements: {
length: 158,
buffer: load_buffer(new Uint16Array([
0, 9, 1, 10, 2, 11, 3, 12, 4,
13, 5, 14, 6, 15, 7, 16, 8, 17, 17,
9, 9, 18, 10, 19, 11, 20, 12, 21, 13,
22, 14, 23, 15, 24, 16, 25, 17, 26, 26,
18, 18, 27, 19, 28, 20, 29, 21, 30, 22,
31, 23, 32, 24, 33, 25, 34, 26, 35, 35,
27, 27, 36, 28, 37, 29, 38, 30, 39, 31,
40, 32, 41, 33, 42, 34, 43, 35, 44, 44,
36, 36, 45, 37, 46, 38, 47, 39, 48, 40,
49, 41, 50, 42, 51, 43, 52, 44, 53, 53,
45, 45, 54, 46, 55, 47, 56, 48, 57, 49,
58, 50, 59, 51, 60, 52, 61, 53, 62, 62,
54, 54, 63, 55, 64, 56, 65, 57, 66, 58,
67, 59, 68, 60, 69, 61, 70, 62, 71, 71,
63, 63, 72, 64, 73, 65, 74, 66, 75, 67,
76, 68, 77, 69, 78, 70, 79, 71, 80,
]), gl.ELEMENT_ARRAY_BUFFER)
}
};
// GL_FLOAT = 4 bytes. 4 * 3 = 12 (color offset), 4 * 5 = 20 (stride)
gl.bindBuffer(gl.ARRAY_BUFFER, cube.vertices);
gl.enableVertexAttribArray(cube.attributes.coord);
gl.vertexAttribPointer(
cube.attributes.coord, 3, gl.FLOAT, gl.FALSE, 20, 0
);
gl.enableVertexAttribArray(cube.attributes.texel);
gl.vertexAttribPointer(
cube.attributes.texel, 2, gl.FLOAT, gl.FALSE, 20, 12
);
// SCIENCE!
var tex = gl.createTexture();
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, tex);
gl.texImage2D(
gl.TEXTURE_2D, 0, gl.RGBA, 4, 4, 0, gl.RGBA, gl.UNSIGNED_BYTE,
new Uint8Array([
0, 0, 0, 0, 0,255, 0,255, 0, 0, 0, 0, 255, 0, 0,255,
255,127, 0,255, 255,255, 0,255, 255,127, 0,255, 255,255, 0,255,
0, 0, 0, 0, 0,255, 0,255, 0, 0, 0, 0, 255, 0, 0,255,
0, 0,255,255, 0,255,255,255, 0, 0,255,255, 255, 0,255,255
])
);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
gl.uniform1i(gl.getUniformLocation(shader, "sampler"), 0);
function cached_static_uniform3fv (pgm, dest) {
var u = gl.getUniformLocation(pgm, dest);
function upload () {
return gl.uniform3fv(u, this);
}
function reinit (d) {
var c = new Float32Array(d);
c.upload = upload.bind(c);
c.reinit = reinit.bind(c);
if (c.length) c.upload();
return c;
}
return reinit([]);
}
function cached_static_uniform_mat4fv (pgm, dest) {
var u = gl.getUniformLocation(pgm, dest);
function upload () {
return gl.uniformMatrix4fv(u, false, this);
}
function reinit (d) {
var c = new Float32Array(d);
c.upload = upload.bind(c);
c.reinit = reinit.bind(c);
if (c.length) c.upload();
return c;
}
return reinit([]);
}
var matrix = {
/* identity matrix does nothing
identity: function () { return new Float32Array([
1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1
])},
*/
// view matrix moves the camera.
view: {
scale: cached_uniform1f(shader, "vScale"),
translate: cached_static_uniform3fv(shader, "vTranslate").reinit([
0, 0, 0
]),
rotate: cached_static_uniform3fv(shader, "vRotate").reinit([
0, 0, 0
])
},
// model matrix moves the object.
// model matrix = scale * translate * rotate
model: {
scale: cached_uniform1f(shader, "mScale"),
translate: cached_static_uniform3fv(shader, "mTranslate").reinit([
0, 0, 0
]),
rotate: cached_static_uniform3fv(shader, "mRotate").reinit([
0, 0, 0
])
},
// projection matrix defines the view frustum
projection: cached_static_uniform_mat4fv(shader, "projection")
};
matrix.view.scale(1);
gl.clearColor(0, 0, 0, 0);
Math.TAU = 2 * Math.PI;
var theta = 0;
var lastframe;
var spinspeed = 1 / 20000;
function doFrame (time) {
if (time - lastframe < 1 / 60) return;
if (fullscreen.active()) {
requestAnimationFrame(doFrame);
if (lastframe) {
theta = theta + (time - lastframe) * spinspeed;
if (Math.TAU <= theta) theta = 0;
}
lastframe = time;
matrix.model.rotate[0] = 3 * theta;
matrix.model.rotate[1] = 5 * theta;
matrix.model.rotate[2] = 7 * theta;
matrix.model.rotate.upload();
}
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
cube.invert(0);
matrix.model.scale(1.0);
gl.drawElements(gl.TRIANGLE_STRIP, cube.elements.length, gl.UNSIGNED_SHORT, 0);
//gl.drawElements(gl.LINE_LOOP, cube.elements.length, gl.UNSIGNED_SHORT, 0);
/*
cube.invert(1);
matrix.model.scale(1.1);
*/
}
var scrollspeed = 1 / 16;
document.onwheel = function (e) {
// this function shamelessly stolen from
// https://stackoverflow.com/users/3225372/martijn
function sign(x) {
return typeof x === 'number' ? x ? x < 0 ? -1 : 1 : x === x ? 0 : NaN : NaN;
}
matrix.view.translate[2] += sign(e.deltaY) * scrollspeed;
matrix.view.translate.upload();
if (!fullscreen.active()) requestAnimationFrame(doFrame);
}
document.onmousemove = function (e) {
if (fullscreen.active()) return;
matrix.model.rotate[0] = Math.PI * (e.clientY / (canvas.height - 1) - .5);
matrix.model.rotate[1] = Math.PI * (e.clientX / (canvas.width - 1) - .5);
matrix.model.rotate[2] = 0;
matrix.model.rotate.upload();
requestAnimationFrame(doFrame);
}
var fullscreen = (function () {
var el = document.documentElement,
fs_off = document.exitFullscreen
|| document.webkitExitFullscreen
|| document.mozCancelFullScreen
|| document.msExitFullscreen,
fs_on = el.requestFullScreen
|| el.webkitRequestFullScreen
|| el.mozRequestFullScreen
|| el.msRequestFullscreen;
return {
off: fs_off.bind(document),
on: fs_on.bind(el),
active: function () {
return (
document.fullscreenElement ||
document.webkitFullscreenElement ||
document.mozFullScreenElement ||
document.msFullscreenElement
);
}
};
})()
onresize = function () {
gl.viewport(0, 0, innerWidth, innerHeight);
canvas.height = innerHeight;
canvas.width = innerWidth;
/* for the matrix
a, b, c, d, <- x in
e, f, g, h, <- y in
i, j, k, l, <- z in
m, n, o, p
| | |
v v v
x y z
o o o
u u u
t t t
x = ax + ey + iz + m
y = bx + fy + jz + n
z = cx + gy + kz + o
multiply vector into matrix horizontally
add vector out of matrix vertically
*/
// for the projection matrix, we must first normalize
// the screen dimensions to somewhere near 1.
var hyp
= Math.sqrt(canvas.width * canvas.width
+ canvas.height * canvas.height);
var zf = -Math.sqrt(2); // because i can
var zn = -0;
var xx = canvas.height / hyp;
var yy = canvas.width / hyp;
var zz = 1 / (zf - zn);
matrix.projection = matrix.projection.reinit([
xx, 0, 0, 0,
0,yy, 0, 0,
0, 0,zz, 0,
0, 0, 0, 1
]);
if (!fullscreen.active()) requestAnimationFrame(doFrame);
}
onresize();
document.onclick = function () {
if (fullscreen.active()) {
fullscreen.off();
lastframe = undefined;
} else {
fullscreen.on();
requestAnimationFrame(doFrame);
}
}
</script></body>
</html>