<!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 vec3 Color;
/* texture mapping happens here */
void main () {
// gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);
gl_FragColor = vec4(Color, 1);
}
</script><script id="vertex" type="x-shader/vertex">
uniform mat4 projection;
attribute vec3 coord;
attribute vec3 color;
varying vec3 Color;
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;
*/
//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"),
color: gl.getAttribLocation(shader, "color")
},
vertices: load_buffer(new Float32Array([
// x, y, z, r, g, b
-4/4,-4/4, 0, 0, 0, 0,
-3/4,-4/4, 0, 0, 1, 0,
-2/4,-4/4, 0, 0, 0, 0,
-1/4,-4/4, 0, 1, 0, 0,
0/4,-4/4, 0, 0, 0, 0,
1/4,-4/4, 0, 0, 1, 0,
2/4,-4/4, 0, 0, 0, 0,
3/4,-4/4, 0, 1, 0, 0,
4/4,-4/4, 0, 0, 0, 0,
-4/4,-3/4, 0, 0, 0, 1,
-3/4,-3/4, 0, 0, 1, 1,
-2/4,-3/4, 0, 0, 0, 1,
-1/4,-3/4, 0, 1, 0, 1,
0/4,-3/4, 0, 0, 0, 1,
1/4,-3/4, 0, 0, 1, 1,
2/4,-3/4, 0, 0, 0, 1,
3/4,-3/4, 0, 1, 0, 1,
4/4,-3/4, 0, 0, 0, 1,
-4/4,-2/4, 0, 0, 0, 0,
-3/4,-2/4, 0, 0, 1, 0,
-2/4,-2/4, 0, 0, 0, 0,
-1/4,-2/4, 0, 1, 0, 0,
0/4,-2/4, 0, 0, 0, 0,
1/4,-2/4, 0, 0, 1, 0,
2/4,-2/4, 0, 0, 0, 0,
3/4,-2/4, 0, 1, 0, 0,
4/4,-2/4, 0, 0, 0, 0,
-4/4,-1/4, 0, 1,.5, 0,
-3/4,-1/4, 0, 1, 1, 0,
-2/4,-1/4, 0, 1,.5, 0,
-1/4,-1/4, 0, 1,.5, 0,
0/4,-1/4, 0, 1,.5, 0,
1/4,-1/4, 0, 1, 1, 0,
2/4,-1/4, 0, 1,.5, 0,
3/4,-1/4, 0, 1,.5, 0,
4/4,-1/4, 0, 1,.5, 0,
-4/4, 0/4, 0, 0, 0, 0,
-3/4, 0/4, 0, 0, 1, 0,
-2/4, 0/4, 0, 0, 0, 0,
-1/4, 0/4, 0, 1, 0, 0,
0/4, 0/4, 0, 0, 0, 0,
1/4, 0/4, 0, 0, 1, 0,
2/4, 0/4, 0, 0, 0, 0,
3/4, 0/4, 0, 1, 0, 0,
4/4, 0/4, 0, 0, 0, 0,
-4/4, 1/4, 0, 0, 0, 1,
-3/4, 1/4, 0, 0, 1, 1,
-2/4, 1/4, 0, 0, 0, 1,
-1/4, 1/4, 0, 1, 0, 1,
0/4, 1/4, 0, 0, 0, 1,
1/4, 1/4, 0, 0, 1, 1,
2/4, 1/4, 0, 0, 0, 1,
3/4, 1/4, 0, 1, 0, 1,
4/4, 1/4, 0, 0, 0, 1,
-4/4, 2/4, 0, 0, 0, 0,
-3/4, 2/4, 0, 0, 1, 0,
-2/4, 2/4, 0, 0, 0, 0,
-1/4, 2/4, 0, 1, 0, 0,
0/4, 2/4, 0, 0, 0, 0,
1/4, 2/4, 0, 0, 1, 0,
2/4, 2/4, 0, 0, 0, 0,
3/4, 2/4, 0, 1, 0, 0,
4/4, 2/4, 0, 0, 0, 0,
-4/4, 3/4, 0, 1,.5, 0,
-3/4, 3/4, 0, 1, 1, 0,
-2/4, 3/4, 0, 1,.5, 0,
-1/4, 3/4, 0, 1,.5, 0,
0/4, 3/4, 0, 1,.5, 0,
1/4, 3/4, 0, 1, 1, 0,
2/4, 3/4, 0, 1,.5, 0,
3/4, 3/4, 0, 1,.5, 0,
4/4, 3/4, 0, 1,.5, 0,
-4/4, 4/4, 0, 0, 0, 0,
-3/4, 4/4, 0, 0, 1, 0,
-2/4, 4/4, 0, 0, 0, 0,
-1/4, 4/4, 0, 1, 0, 0,
0/4, 4/4, 0, 0, 0, 0,
1/4, 4/4, 0, 0, 1, 0,
2/4, 4/4, 0, 0, 0, 0,
3/4, 4/4, 0, 1, 0, 0,
4/4, 4/4, 0, 0, 0, 0
]), gl.ARRAY_BUFFER),
elements: {
length: 200,
buffer: load_buffer(new Uint16Array([
0, 9, 1, 10, 1, 11, 2,
11, 3, 12, 3, 13, 4,
13, 5, 14, 5, 15, 6,
15, 7, 16, 7, 17, 8,
26, 17, 25, 16, 25, 15, 24,
15, 23, 14, 23, 13, 22,
13, 21, 12, 21, 11, 20,
11, 19, 10, 19, 9, 18,
27, 19, 28, 19, 29, 20, 29,
21, 30, 21, 31, 22, 31,
23, 32, 23, 33, 24, 33,
25, 34, 25, 35, 26, 35,
44, 35, 43, 34, 43, 33, 42,
33, 41, 32, 41, 31, 40,
31, 39, 30, 39, 29, 38,
29, 37, 28, 37, 27, 36,
//100
45, 37, 46, 37, 47, 38, 47,
39, 48, 39, 49, 40, 49,
41, 50, 41, 51, 42, 51,
43, 52, 43, 53, 44, 53,
62, 53, 61, 52, 61, 51, 60,
51, 59, 50, 59, 49, 58,
49, 57, 48, 57, 47, 56,
47, 55, 46, 55, 45, 54,
63, 55, 64, 55, 65, 56, 65,
57, 66, 57, 67, 58, 67,
59, 68, 59, 69, 60, 69,
61, 70, 61, 71, 62, 71,
80, 71, 79, 70, 79, 69, 78,
69, 77, 68, 77, 67, 76,
67, 75, 66, 75, 65, 74,
65, 73, 64, 73, 63, 72
//200
]), gl.ELEMENT_ARRAY_BUFFER)
}
};
// GL_FLOAT = 4 bytes. 4 * 3 = 12 (color offset), 4 * 6 = 24 (stride)
gl.bindBuffer(gl.ARRAY_BUFFER, cube.vertices);
gl.enableVertexAttribArray(cube.attributes.coord);
gl.vertexAttribPointer(
cube.attributes.coord, 3, gl.FLOAT, gl.FALSE, 24, 0
);
gl.enableVertexAttribArray(cube.attributes.color);
gl.vertexAttribPointer(
cube.attributes.color, 3, gl.FLOAT, gl.FALSE, 24, 12
);
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);
/*
cube.invert(1);
matrix.model.scale(1.1);
gl.drawElements(gl.LINE_LOOP, cube.elements.length, gl.UNSIGNED_SHORT, 0);
*/
}
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;
console.log(matrix.view.translate[2]);
matrix.view.translate.upload();
if (!fullscreen.active()) requestAnimationFrame(doFrame);
}
/*
document.onmousemove = function (e) {
var cw = canvas.width / 2;
var ch = canvas.height / 2;
var sc = (ch + cw) / 2;
matrix.view.rotate[1] = (e.clientX - cw) / sc;
matrix.view.rotate[0] = (e.clientY - ch) / sc;
matrix.view.rotate.upload();
if (!fullscreen.active()) 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>