Created
September 15, 2020 14:18
-
-
Save equalent/d9f8d727a502292cf47b7af0eb0e07fc to your computer and use it in GitHub Desktop.
Simple JS + WebGL Mandelbrot set implementation
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> | |
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>MANDELBROT</title> | |
| </head> | |
| <body style="font-family: sans-serif;"> | |
| <canvas id="cnv" style="width: 100%; height: 100%; position: fixed; top: 0; left: 0;"></canvas> | |
| <div style="position: fixed; z-index: 100; top: 20px; left: 20px; color: white; background-color: rgba(100, 100, 100, 0.2);"> | |
| <h3>Mandelbrot set</h3> | |
| <label for="volume">Iteration count</label> | |
| <input type="range" id="inIter" min="5" max="500" value="100"> | |
| </div> | |
| <script id="vertexShader" type="x-shader/x-vertex"> | |
| attribute vec2 vPos; | |
| attribute vec2 vUv; | |
| varying highp vec2 uv; | |
| void main(void){ | |
| gl_Position = vec4(vPos, 0.0, 1.0); | |
| uv = vUv; | |
| } | |
| </script> | |
| <script id="fragShader" type="x-shader/x-fragment"> | |
| varying highp vec2 uv; | |
| uniform int inIter; | |
| highp float cmulRe(highp float Re1, highp float Im1, highp float Re2, highp float Im2){ | |
| return (Re1 * Re2) - (Im1 * Im2); | |
| } | |
| highp float cmulIm(highp float Re1, highp float Im1, highp float Re2, highp float Im2){ | |
| return (Re1 * Im2) + (Im1 * Re2); | |
| } | |
| highp float cabs(highp float Re, highp float Im){ | |
| return sqrt(Re*Re + Im*Im); | |
| } | |
| highp float getGray(highp float cRe, highp float cIm){ | |
| highp float zRe, zIm, tRe, tIm; | |
| zRe = 0.0; | |
| zIm = 0.0; | |
| tRe = 0.0; | |
| tIm = 0.0; | |
| for(int i = 0; i < 500; i++){ | |
| if(i >= inIter)break; | |
| if(cabs(zRe, zIm) > 2.0){ | |
| return (float(i) / float(inIter)); | |
| } | |
| tRe = zRe; | |
| tIm = zIm; | |
| zRe = cmulRe(tRe, tIm, tRe, tIm); | |
| zIm = cmulIm(tRe, tIm, tRe, tIm); | |
| zRe += cRe; | |
| zIm += cIm; | |
| } | |
| return 1.0; | |
| } | |
| void main(void){ | |
| highp float gray = getGray(uv.x, uv.y); | |
| gl_FragColor = vec4(gray, gray, gray, 1.0); | |
| } | |
| </script> | |
| <script> | |
| var ibo, vbo; | |
| var ps, fs, prog; | |
| var posId, uvId; | |
| var iterUid; | |
| let elInIter = document.getElementById("inIter"); | |
| function update(cnv, gl) { | |
| cnv.width = window.innerWidth; | |
| cnv.height = window.innerHeight; | |
| gl.viewport(0, 0, cnv.width, cnv.height); | |
| gl.clearColor(0.0, 0.0, 0.0, 1.0); | |
| gl.clear(gl.COLOR_BUFFER_BIT); | |
| gl.viewport(0, 0, cnv.width, cnv.height); | |
| gl.uniform1i(iterUid, parseInt(elInIter.value)); | |
| gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0); | |
| } | |
| function main(cnv) { | |
| let gl = cnv.getContext('webgl'); | |
| console.log(gl); | |
| var vertices = [ | |
| -1, -1, -2, -1.1, | |
| -1, 1, -2, 1.1, | |
| 1, 1, 1, 1.1, | |
| 1, -1, 1, -1.1, | |
| ]; | |
| var indices = [ | |
| 0, 1, 2, 0, 2, 3 | |
| ]; | |
| vbo = gl.createBuffer(); | |
| gl.bindBuffer(gl.ARRAY_BUFFER, vbo); | |
| gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW); | |
| ibo = gl.createBuffer(); | |
| gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, ibo); | |
| gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(indices), gl.STATIC_DRAW); | |
| let vsCode = document.getElementById('vertexShader').innerHTML; | |
| vs = gl.createShader(gl.VERTEX_SHADER); | |
| gl.shaderSource(vs, vsCode); | |
| gl.compileShader(vs); | |
| let fsCode = document.getElementById('fragShader').innerHTML; | |
| fs = gl.createShader(gl.FRAGMENT_SHADER); | |
| gl.shaderSource(fs, fsCode); | |
| gl.compileShader(fs); | |
| console.log(gl.getShaderInfoLog(fs)); | |
| prog = gl.createProgram(); | |
| gl.attachShader(prog, vs); | |
| gl.attachShader(prog, fs); | |
| gl.linkProgram(prog); | |
| console.log(gl.getProgramInfoLog(prog)); | |
| gl.useProgram(prog); | |
| posId = gl.getAttribLocation(prog, 'vPos'); | |
| uvId = gl.getAttribLocation(prog, 'vUv'); | |
| iterUid = gl.getUniformLocation(prog, 'inIter'); | |
| gl.vertexAttribPointer(posId, 2, gl.FLOAT, false, 16, 0); | |
| gl.vertexAttribPointer(uvId, 2, gl.FLOAT, false, 16, 8); | |
| gl.enableVertexAttribArray(posId); | |
| gl.enableVertexAttribArray(uvId); | |
| gl.enable(gl.DEPTH_TEST); | |
| setInterval(() => { update(cnv, gl); }, 50); | |
| } | |
| window.addEventListener("load", () => { | |
| let cnv = document.getElementById('cnv'); | |
| main(cnv); | |
| }); | |
| </script> | |
| </body> | |
| </html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment