sigrist-svelte-mandelbrot-explorer
Version:
A svelte component that renders the mandelbrot set using WebGL. Try this [Demo](https://sigrist.dev/apps/mandelbrot).
112 lines (111 loc) • 4.76 kB
JavaScript
import vertexShaderCode from './shaders/vertexShader';
import fragmentShaderCode from './shaders/fragmentShader';
/**
* Renders the Mandelbrot set to the canvas Element
*
*/
function mandelbrotAction(node, args) {
/**Access to the WebGLRenderingContext interface.
* @readonly
* @type {WebGLRenderingContext}*/
const gl = node.getContext("webgl");
if (!gl) {
console.error("MANDELBROT: Your browser does not support WebGL");
return;
}
let { center, magnificationMultiplier, width, height, numberOfIterations } = args;
node.width = width;
node.height = height;
let program;
let angleOffset = 0.0;
let time = Date.now();
function updateAngleOffset() {
const newTime = Date.now();
angleOffset += (newTime - time) / 250;
time = newTime;
}
//Initialize and Bind Vertex Buffer for Quad
const vertexBuffer = gl.createBuffer();
const indexBuffer = gl.createBuffer();
const vertices = [-1.0, 1.0, -1.0, -1.0, 1.0, -1.0, 1.0, 1.0];
const indecies = [3, 2, 1, 3, 1, 0];
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(indecies), gl.STATIC_DRAW);
/**Creates a WebGL Program and sets the program variable to it.
* If Program is already set, it will delete it and create a new Program
*/
function configureProgram() {
const vertexShader = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vertexShader, vertexShaderCode);
gl.compileShader(vertexShader);
if (!gl.getShaderParameter(vertexShader, gl.COMPILE_STATUS)) {
console.error("Could not compile vertex shader", gl.getShaderInfoLog(vertexShader));
gl.deleteShader(vertexShader);
}
const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(fragmentShader, fragmentShaderCode);
gl.compileShader(fragmentShader);
if (!gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS)) {
console.error("Could not compile fragment shader", gl.getShaderInfoLog(fragmentShader));
gl.deleteShader(fragmentShader);
}
if (program) {
gl.deleteProgram(program);
program = null;
}
program = gl.createProgram();
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
gl.linkProgram(program);
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
console.error("Could not link program");
gl.deleteProgram(program);
}
gl.useProgram(program);
gl.deleteShader(fragmentShader);
gl.deleteShader(vertexShader);
}
configureProgram();
/**Render the WebGL Context with the current program */
function render() {
//Map vertex data to vertex shader attributes
const positionAttributeLocation = gl.getAttribLocation(program, "position");
gl.vertexAttribPointer(positionAttributeLocation, 2, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(positionAttributeLocation);
//Set uniforms
const translationMatrixPosition = gl.getUniformLocation(program, "translation_matrix");
gl.uniformMatrix3fv(translationMatrixPosition, false, [(width / height) * magnificationMultiplier, 0, 0, 0, 1 * magnificationMultiplier, 0, ...center, 1]);
const angleOffsetPosition = gl.getUniformLocation(program, "angleOffset");
gl.uniform1f(angleOffsetPosition, angleOffset);
const maximumIterationsPosition = gl.getUniformLocation(program, "maximumIterations");
gl.uniform1f(maximumIterationsPosition, numberOfIterations);
gl.clearColor(0, 0, 1, 1);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
gl.viewport(0, 0, width, height);
gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0);
updateAngleOffset();
requestAnimationFrame(render);
}
requestAnimationFrame(render);
return {
update(newArgs) {
if (!gl)
return;
center = newArgs.center;
magnificationMultiplier = newArgs.magnificationMultiplier;
width = newArgs.width;
height = newArgs.height;
numberOfIterations = newArgs.numberOfIterations;
node.width = width;
node.height = height;
},
destroy() {
gl.deleteBuffer(vertexBuffer);
gl.deleteBuffer(indexBuffer);
gl.deleteProgram(program);
}
};
}
export default mandelbrotAction;