Last active
January 31, 2025 23:47
-
-
Save Garciat/08640e24c907cc627eca to your computer and use it in GitHub Desktop.
WebGL Mandelbrot // Draws the Mandelbrot set using WebGL.
This file contains 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> | |
<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