Click and drag to seed.
Created
June 27, 2014 20:38
-
-
Save robinhouston/9868d9c8d06cad448bf0 to your computer and use it in GitHub Desktop.
Interactive maze generator
This file contains hidden or 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> | |
<meta charset="utf-8"> | |
<title>Interactive reaction-diffusion simulator</title> | |
<style> | |
body { font-family: sans-serif; } | |
canvas { display: block; margin: auto; cursor: crosshair; border: 1px solid #666; width: 1024px; height: 512px; border: 10px solid #000 } | |
#fail { color: red; white-space: pre-wrap; max-width: 600px; } | |
</style> | |
<script type="x-webgl/x-vertex-shader" id="vertex-shader"> | |
attribute vec2 a_position; | |
void main() { | |
gl_Position = vec4(a_position, 0, 1); | |
} | |
</script> | |
<script type="x-webgl/x-fragment-shader" id="timestep-shader"> | |
precision mediump float; | |
uniform sampler2D u_image; | |
uniform vec2 u_size; | |
uniform vec2 u_mouse_position; | |
const float // Parameters from Ready: Patterns > FitzHugh-Nagomo > ising_regime.vti | |
A0 = -0.01, | |
A1 = 2.0, | |
EPSILON = 0.1, | |
DELTA = 7.0; | |
const float TIMESTEP = 0.03, | |
SCALE = 1.0; | |
void main() { | |
vec2 p = gl_FragCoord.xy, | |
n = p + vec2(0.0, 1.0), | |
e = p + vec2(1.0, 0.0), | |
s = p + vec2(0.0, -1.0), | |
w = p + vec2(-1.0, 0.0); | |
vec2 val = texture2D(u_image, p / u_size).xy, | |
laplacian = texture2D(u_image, n / u_size).xy | |
+ texture2D(u_image, e / u_size).xy | |
+ texture2D(u_image, s / u_size).xy | |
+ texture2D(u_image, w / u_size).xy | |
- 4.0 * val; | |
vec2 delta = vec2(val.x - val.x*val.x*val.x - val.y + laplacian.x * SCALE, | |
EPSILON*(val.x - A1*val.y - A0) + DELTA*laplacian.y * SCALE); | |
if (u_mouse_position.x >= 0.0 && distance(u_mouse_position.xy/2.0, gl_FragCoord.xy) <= 3.0) { | |
gl_FragColor = vec4(0.99, 0.3, 0, 0); | |
} else { | |
gl_FragColor = vec4(val + delta * TIMESTEP, 0, 0); | |
} | |
} | |
</script> | |
<script type="x-webgl/x-fragment-shader" id="render-shader"> | |
precision mediump float; | |
uniform sampler2D u_image; | |
uniform vec2 u_size; | |
const float COLOR_MIN = -0.10, COLOR_MAX = -0.05; | |
void main() { | |
float c = texture2D(u_image, gl_FragCoord.xy / u_size).y, | |
v = max(0.0, min(1.0, (c - COLOR_MIN) / (COLOR_MAX - COLOR_MIN))); | |
gl_FragColor = vec4(v, v, v, 1); | |
} | |
</script> | |
<body> | |
<script> | |
var W = 512, H = 256; | |
function init() { | |
if (window.parent && window.parent != window) { | |
var iframe = window.parent.document.querySelector("iframe"); | |
iframe.style.width = "1044px"; | |
iframe.style.height = "532px"; | |
} | |
var canvas = document.createElement("canvas"); | |
canvas.id = "canvas"; | |
canvas.width = W; | |
canvas.height = H; | |
document.body.appendChild(canvas); | |
var gl = canvas.getContext("webgl") || canvas.getContext("experimental-webgl"); | |
checkCompatibility(gl); | |
var vertex_shader = createShader(gl, gl.VERTEX_SHADER, "vertex-shader"), | |
timestep_shader = createShader(gl, gl.FRAGMENT_SHADER, "timestep-shader"), | |
render_shader = createShader(gl, gl.FRAGMENT_SHADER, "render-shader"); | |
var timestep_prog = createAndLinkProgram(gl, vertex_shader, timestep_shader), | |
render_prog = createAndLinkProgram(gl, vertex_shader, render_shader); | |
gl.useProgram(render_prog); | |
loadVertexData(gl, render_prog); | |
gl.uniform2f(gl.getUniformLocation(render_prog, "u_size"), W, H); | |
gl.useProgram(timestep_prog); | |
loadVertexData(gl, timestep_prog); | |
gl.uniform2f(gl.getUniformLocation(timestep_prog, "u_size"), W, H); | |
var u_mouse_position = gl.getUniformLocation(timestep_prog, "u_mouse_position"); | |
gl.uniform2f(u_mouse_position, -1, -1); | |
var initial_state = getInitialState(); | |
var t1 = newTexture(gl, initial_state), | |
t2 = newTexture(gl, null), | |
fb1 = newFramebuffer(gl, t1), | |
fb2 = newFramebuffer(gl, t2); | |
// Check the hardware can render to a float framebuffer | |
// (https://developer.mozilla.org/en-US/docs/Web/WebGL/WebGL_best_practices) | |
gl.useProgram(timestep_prog); | |
gl.bindFramebuffer(gl.FRAMEBUFFER, fb1); | |
var fb_status = gl.checkFramebufferStatus(gl.FRAMEBUFFER); | |
if (fb_status != gl.FRAMEBUFFER_COMPLETE) { | |
fail("Cannot render to framebuffer: " + fb_status); | |
} | |
function step() { | |
gl.useProgram(timestep_prog); | |
for (var i=0; i<40; i++) { | |
gl.bindTexture(gl.TEXTURE_2D, [t1, t2][i % 2]); | |
gl.bindFramebuffer(gl.FRAMEBUFFER, [fb2, fb1][i % 2]); | |
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4); | |
} | |
gl.useProgram(render_prog); | |
gl.bindTexture(gl.TEXTURE_2D, t1); | |
gl.bindFramebuffer(gl.FRAMEBUFFER, null); | |
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4); | |
} | |
var af; | |
function frame() { | |
step(); | |
af = requestAnimationFrame(frame); | |
} | |
af = requestAnimationFrame(frame); | |
var canvas_rect = canvas.getBoundingClientRect(); | |
window.addEventListener("resize", function() { | |
canvas_rect = canvas.getBoundingClientRect(); | |
}); | |
function registerMouse(e) { | |
var x = e.clientX - canvas_rect.left - 10, | |
y = canvas_rect.height - 1 - (e.clientY - canvas_rect.top) - 10; | |
gl.useProgram(timestep_prog); | |
gl.uniform2f(u_mouse_position, x, y); | |
} | |
function unregisterMouse() { | |
gl.useProgram(timestep_prog); | |
gl.uniform2f(u_mouse_position, -1, -1); | |
canvas.removeEventListener("mousemove", registerMouse, false); | |
} | |
canvas.addEventListener("mousedown", function(e) { | |
canvas.addEventListener("mousemove", registerMouse, false); | |
registerMouse(e); | |
}); | |
canvas.addEventListener("mouseup", unregisterMouse); | |
canvas.addEventListener("mouseout", unregisterMouse); | |
} | |
function getInitialState() { | |
var a = new Float32Array(4 * W * H); | |
for (var i=0; i<W*H; i++) { | |
a[4*i + 0] = -0.7; | |
a[4*i + 0] = -0.3; | |
} | |
return a; | |
} | |
// Create, initialise, and bind a new texture | |
function newTexture(gl, initial_state) { | |
var texture = gl.createTexture(); | |
gl.bindTexture(gl.TEXTURE_2D, texture); | |
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST); | |
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST); | |
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.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, W, H, 0, gl.RGBA, gl.FLOAT, initial_state); | |
return texture; | |
} | |
function newFramebuffer(gl, texture) { | |
var fb = gl.createFramebuffer(); | |
gl.bindFramebuffer(gl.FRAMEBUFFER, fb); | |
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0); | |
return fb; | |
} | |
function loadVertexData(gl, prog) { | |
gl.bindBuffer(gl.ARRAY_BUFFER, gl.createBuffer()); | |
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([ -1,-1, 1,-1, -1,1, 1,1 ]), gl.STATIC_DRAW); | |
var a_position = gl.getAttribLocation(prog, "a_position"); | |
gl.enableVertexAttribArray(a_position); | |
gl.vertexAttribPointer(a_position, 2, gl.FLOAT, false, 0, 0); | |
} | |
function createAndLinkProgram(gl, vertex_shader, fragment_shader) { | |
var prog = gl.createProgram(); | |
gl.attachShader(prog, vertex_shader); | |
gl.attachShader(prog, fragment_shader); | |
gl.linkProgram(prog); | |
if (!gl.getProgramParameter(prog, gl.LINK_STATUS)) { | |
fail("Failed to link program: " + gl.getProgramInfoLog(prog)); | |
} | |
return prog; | |
} | |
function createShader(gl, shader_type, shader_code_id) { | |
var shader = gl.createShader(shader_type); | |
gl.shaderSource(shader, document.getElementById(shader_code_id).text); | |
gl.compileShader(shader); | |
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) { | |
var err = gl.getShaderInfoLog(shader); | |
fail("Failed to compile shader: " + err); | |
} | |
return shader | |
} | |
function checkCompatibility(gl) { | |
if (!gl) fail("WebGL is not supported"); | |
var float_texture_ext = gl.getExtension("OES_texture_float"); | |
if (!float_texture_ext) fail("Your browser does not support the WebGL extension OES_texture_float"); | |
window.float_texture_ext = float_texture_ext; // Hold onto it | |
var max_texture_size = gl.getParameter(gl.MAX_TEXTURE_SIZE); | |
if (max_texture_size < 512) fail("Your browser only supports "+max_texture_size+"×"+max_texture_size+" WebGL textures"); | |
} | |
function fail(message) { | |
var fail = document.createElement("p"); | |
fail.id = "fail"; | |
fail.appendChild(document.createTextNode(message)); | |
document.body.removeChild(document.getElementById("canvas")); | |
document.body.appendChild(fail); | |
throw message; | |
} | |
init(); | |
</script> | |
</body> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment