Skip to content

Instantly share code, notes, and snippets.

@Garciat
Last active October 31, 2015 23:08
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Garciat/08640e24c907cc627eca to your computer and use it in GitHub Desktop.
Save Garciat/08640e24c907cc627eca to your computer and use it in GitHub Desktop.
<!DOCTYPE html>
<html>
<head>
<title>WebGL Mandelbrot</title>
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<meta http-equiv="content-type" content="text/html; charset=ISO-8859-1">
<style>
html, body {
height: 100%;
margin: 0;
}
</style>
<script id="shader-fs" type="x-shader/x-fragment">
precision highp float;
uniform vec2 u_planep;
uniform float u_iscale;
uniform int u_maxiter;
void main(void)
{
vec2 p = gl_FragCoord.xy;
float x0 = u_planep.x + p.x * u_iscale;
float y0 = u_planep.y + p.y * u_iscale;
float x = 0.0;
float y = 0.0;
float x2;
float y2;
float tmpx;
int it = 0;
vec3 col = vec3(0.0, 0.0, 0.0);
int runaway = 0;
for (int i = 0; i < 256; ++i)
{
x2 = x * x;
y2 = y * y;
tmpx = x2 - y2 + x0;
y = 2.0*x*y + y0;
x = tmpx;
if (i > u_maxiter) break;
if (x2 + y2 >= 4.0)
{
it = i;
runaway = 1;
break;
}
}
if (runaway == 1)
{
float smooth = float(it) - log2(log2(x*x+y*y)) + 4.0;
col += 0.5 + 0.5*cos( 3.0 + smooth*0.15 + vec3(0.0,0.6,1.0));
gl_FragColor = vec4( col, 1.0 );
}
else
{
gl_FragColor = vec4( 0.0, 0.0, 0.0, 1.0 );
}
}
</script>
<script id="shader-vs" type="x-shader/x-vertex">
attribute vec2 aVertexPosition;
void main(void)
{
gl_Position = vec4(aVertexPosition, 1.0, 1.0);
}
</script>
<script>
var gl;
function initGL(canvas)
{
try {
gl = canvas.getContext("experimental-webgl");
gl.viewport(0, 0, canvas.width, canvas.height);
} catch(e) {
}
if (!gl) {
alert("Could not initialise WebGL, sorry :-(");
}
}
function getShader(gl, id)
{
var shaderScript = document.getElementById(id);
if (!shaderScript)
return null;
var shader;
if (shaderScript.type == "x-shader/x-fragment")
{
shader = gl.createShader(gl.FRAGMENT_SHADER);
}
else if (shaderScript.type == "x-shader/x-vertex")
{
shader = gl.createShader(gl.VERTEX_SHADER);
}
else
{
return null;
}
gl.shaderSource(shader, shaderScript.textContent);
gl.compileShader(shader);
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS))
{
alert(gl.getShaderInfoLog(shader));
return null;
}
return shader;
}
var shaderProgram;
var aVertexPosition;
function initShaders()
{
var fragmentShader = getShader(gl, "shader-fs");
var vertexShader = getShader(gl, "shader-vs");
shaderProgram = gl.createProgram();
gl.attachShader(shaderProgram, vertexShader);
gl.attachShader(shaderProgram, fragmentShader);
gl.linkProgram(shaderProgram);
if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS))
{
alert("Could not initialise shaders");
}
gl.useProgram(shaderProgram);
aVertexPosition = gl.getAttribLocation(shaderProgram, "aVertexPosition");
gl.enableVertexAttribArray(aVertexPosition);
u_planep = gl.getUniformLocation(shaderProgram, "u_planep");
u_iscale = gl.getUniformLocation(shaderProgram, "u_iscale");
u_maxiter = gl.getUniformLocation(shaderProgram, "u_maxiter");
}
function initBuffers()
{
vertexPositionBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vertexPositionBuffer);
var vertices = [
-1.0, -1.0,
1.0, -1.0,
-1.0, 1.0,
-1.0, 1.0,
1.0, -1.0,
1.0, 1.0
];
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
gl.vertexAttribPointer(aVertexPosition, 2, gl.FLOAT, false, 0, 0);
}
function drawScene()
{
gl.drawArrays(gl.TRIANGLES, 0, 6);
}
var canvas;
function webGLStart()
{
canvas = document.getElementById("canvas");
canvas.width = document.body.clientWidth;
canvas.height = document.body.clientHeight;
initGL(canvas);
initShaders()
gl.clearColor(0.0, 0.0, 0.0, 1.0);
gl.clearDepth(1.0);
initBuffers();
}
function setMaxIter(val) {
gl.uniform1i(u_maxiter, val);
drawScene();
}
window.addEventListener('load', function () {
webGLStart();
drawScene();
var CLICKZOOM = 3;
var SCREENW = canvas.width;
var SCREENH = canvas.height;
var DOMAINX = -2.5;
var DOMAINY = -1.1;
var DOMAINW = 3.5;
var DOMAINH = 2.2;
var SCALE = Math.min(
SCREENW / DOMAINW,
SCREENH / DOMAINH
);
var ISCALE = 1.0 / SCALE;
var PLANEX = DOMAINX - (SCREENW - DOMAINW * SCALE) * ISCALE / 2.0;
var PLANEY = DOMAINY - (SCREENH - DOMAINH * SCALE) * ISCALE / 2.0;
gl.uniform2f(u_planep, PLANEX, PLANEY);
gl.uniform1f(u_iscale, ISCALE);
gl.uniform1i(u_maxiter, 256);
drawScene();
canvas.addEventListener('click', function zoom(ev) {
var px = ev.offsetX;
var py = SCREENH - ev.offsetY;
var scale_ = CLICKZOOM * SCALE;
var planeW_ = SCREENW / scale_;
var planeH_ = SCREENH / scale_;
var planeX_ = PLANEX + px * ISCALE - planeW_ / 2;
var planeY_ = PLANEY + py * ISCALE - planeH_ / 2;
planeX_ += (SCREENW / 2 - px) / scale_;
planeY_ += (SCREENH / 2 - py) / scale_;
PLANEX = planeX_;
PLANEY = planeY_;
SCALE = scale_;
ISCALE = 1.0 / SCALE;
gl.uniform2f(u_planep, PLANEX, PLANEY);
gl.uniform1f(u_iscale, ISCALE);
drawScene();
});
});
</script>
</head>
<body>
<div>
<div style="position:absolute;top:0;left:0;right:0;z-index:1;text-align:center; padding: 1em;">
<input type="range" min="1" step="1" max="256" value="256" onchange="setMaxIter(parseInt(this.value, 10))">
</div>
</div>
<canvas id="canvas" style="position:absolute;top:0;left:0"></canvas>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment