<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<link rel="icon" src="/images/favicon.gif" />
<style type="text/css">
* {
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
-ms-box-sizing: border-box;
-o-box-sizing: border-box;
box-sizing: border-box;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
-o-user-select: none;
user-select: none;
background: #000;
display: block;
padding: 0;
margin: 0;
border: 0;
height: 100%;
width: 100%;
text-align: center;
}
head { display: none; }
canvas {
image-rendering: pixelated;
height: 100%;
background: #f00;
margin: auto;
//border: 1px solid white;
}
</style>
<script type="x-shader/fragment" id="fs2">
precision mediump float;
varying vec4 sample;
void main () {
// gl_FragColor = sample;
gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0 / 256.0);
}
</script>
<script type="x-shader/vertex" id="vs2">
attribute vec2 coord;
uniform sampler2D buffer;
varying vec4 sample;
void main () {
vec4 data = texture2D(buffer, coord);
sample = data;
// gl_Position = vec4(2.0 * coord - 1.0, 0.0, 1.0);
gl_Position = vec4(0.5 * data.g, -0.5 * data.r, 0.0, 1.0);
}
</script>
<script type="x-shader/fragment" id="fs1">
precision mediump float;
uniform sampler2D buffer;
varying vec2 Coord;
varying vec2 Seed;
void main () {
vec4 tmp = texture2D(buffer, 0.5 * Coord - 0.5);
vec2 z = tmp.rg;
vec2 c = tmp.ba;
if (vec2(0.0, 0.0) == c) c = 2.0 * Seed;
vec2 square = z * z;
z.y = 2.0 * z.x * z.y + c.y;
z.x = square.x - square.y + c.x;
if (16.0 < square.x + square.y) c = 2.0 * Seed;
gl_FragColor = vec4(z, c);
}
</script>
<script type="x-shader/vertex" id="vs1">
attribute vec2 seed;
attribute vec2 coord;
varying vec2 Seed;
varying vec2 Coord;
void main () {
Seed = seed;
Coord = coord;
gl_Position = vec4(coord, 0.0, 1.0);
}
</script>
<script type="text/javascript">
function compileShader (ctx, type, id) {
var shader = ctx.createShader(type);
console.log("compiling "+id);
ctx.shaderSource(shader, document.getElementById(id).innerHTML);
ctx.compileShader(shader);
if (!ctx.getShaderParameter(shader, ctx.COMPILE_STATUS))
console.log(ctx.getShaderInfoLog(shader));
else console.log("successfully compiled "+id+" shader");
return shader;
}
function makeShaderProgram (ctx, v_id, f_id) {
var shader = ctx.createProgram();
var vertex = compileShader(ctx, ctx.VERTEX_SHADER, v_id);
var fragment = compileShader(ctx, ctx.FRAGMENT_SHADER, f_id);
ctx.attachShader(shader, vertex);
ctx.attachShader(shader, fragment);
ctx.linkProgram(shader);
if (!ctx.getProgramParameter(shader, ctx.LINK_STATUS))
console.log(ctx.getProgramInfoLog(shader));
else console.log("successfully linked program (" + v_id + ", " + f_id + ")");
ctx.deleteShader(vertex);
ctx.deleteShader(fragment);
return shader;
}
function selectShader (ctx, shader) {
ctx.useProgram(shader.prog);
ctx.enableVertexAttribArray(shader.coord);
ctx.vertexAttribPointer(shader.coord, 2, ctx.FLOAT, ctx.FALSE, 8, 0);
}
function makeTexture (ctx, x, y, type) {
var tex = ctx.createTexture();
ctx.bindTexture(ctx.TEXTURE_2D, tex);
ctx.texImage2D(ctx.TEXTURE_2D, 0, ctx.RGBA, x, y, 0, ctx.RGBA, type, null);
ctx.texParameteri(ctx.TEXTURE_2D, ctx.TEXTURE_MAG_FILTER, ctx.NEAREST);
ctx.texParameteri(ctx.TEXTURE_2D, ctx.TEXTURE_MIN_FILTER, ctx.NEAREST);
return tex;
}
addEventListener("load", function load (e) {
removeEventListener("load", load, false);
var canvas = document.getElementsByTagName("canvas")[0];
var gl = canvas.getContext("webgl", {preserveDrawingBuffer: true})
|| canvas.getContext("experimental-webgl", {preserveDrawingBuffer: true});
if (!gl) alert("Sorry, you need WebGL for this.");
if (gl.getExtension("OES_texture_float"))
console.log("Enabled OES_texture_float extension");
onresize = function () {
canvas.height = innerHeight;
canvas.width = innerWidth;
}
onresize();
var shaders = {
iterate: { prog: makeShaderProgram(gl, "vs1", "fs1") },
draw: { prog: makeShaderProgram(gl, "vs2", "fs2") }
};
shaders.iterate.coord = gl.getAttribLocation(shaders.iterate.prog, "coord");
shaders.iterate.seed = gl.getAttribLocation(shaders.iterate.prog, "seed");
shaders.draw.coord = gl.getAttribLocation(shaders.draw.prog, "coord");
var square = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, square);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
-1,-1,-1, 1, 1,-1, 1, 1
]), gl.STATIC_DRAW);
var texSize = 64;
var textures = [
makeTexture(gl, texSize, texSize, gl.FLOAT),
makeTexture(gl, texSize, texSize, gl.FLOAT)
];
var fbo = gl.createFramebuffer();
window.tmp = [];
for (var y = 0; y < texSize; y++)
for (var x = 0; x < texSize; x++)
tmp.push(
x / (texSize - 1),
y / (texSize - 1)
);
var texCoords = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, texCoords);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(tmp), gl.STATIC_DRAW);
var seeds = gl.createBuffer();
var which = 0;
var lastframe = 0;
gl.clearColor(0, 0, 0, 1);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
gl.blendFunc(gl.ONE, gl.ONE);
gl.blendEquation(gl.FUNC_ADD);
var i = 0;
draw = function (time) {
// skip a frame if we're being called too fast
if (time - lastframe < 1000 / 60)
return setTimeout(draw, 10);
lastframe = time;
gl.useProgram(shaders.iterate.prog);
window.tmp = [];
for (var y = 0; y < texSize; y++)
for (var x = 0; x < texSize; x++)
tmp.push(Math.random(), Math.random());
gl.bindBuffer(gl.ARRAY_BUFFER, seeds);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(tmp), gl.STATIC_DRAW);
gl.vertexAttribPointer(shaders.iterate.seed, 2, gl.FLOAT, gl.FALSE, 8, 0);
gl.enableVertexAttribArray(shaders.iterate.seed);
//tmp = undefined;
gl.bindBuffer(gl.ARRAY_BUFFER, square);
gl.vertexAttribPointer(shaders.iterate.coord, 2, gl.FLOAT, gl.FALSE, 8, 0);
gl.enableVertexAttribArray(shaders.iterate.coord);
gl.bindTexture(gl.TEXTURE_2D, textures[(which + 1) & 1]);
gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
gl.framebufferTexture2D(
gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0,
gl.TEXTURE_2D, textures[which], 0
);
gl.viewport(0, 0, texSize, texSize);
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
gl.bindBuffer(gl.ARRAY_BUFFER, texCoords);
selectShader(gl, shaders.draw);
gl.bindTexture(gl.TEXTURE_2D, textures[which]);
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
gl.enable(gl.BLEND);
gl.viewport(0, 0, innerWidth, innerHeight);
gl.drawArrays(gl.POINTS, 0, texSize * texSize);
gl.disable(gl.BLEND);
which = (which + 1) & 1;
//if (++i < texSize * 2)
// requestAnimationFrame(draw);
//else console.log("done");
}
draw();
});
</script>
</head>
<body>
<canvas></canvas>
</body>
</html>