gl.js
3.0 kB · javascript · 99 lines
1const VERT = `#version 300 es2in vec2 a_position;3void main() { gl_Position = vec4(a_position, 0.0, 1.0); }4`;56const FRAG = `#version 300 es7precision highp float;8uniform vec2 u_resolution;9uniform vec4 u_viewport;10uniform vec2 u_c;11uniform bool u_julia;12uniform int u_maxIter;13uniform vec3 u_primary;14uniform vec3 u_accent;15uniform float u_rotation;16uniform float u_time;17out vec4 fragColor;18void main() {19 vec2 uv = gl_FragCoord.xy / u_resolution;20 float px = mix(u_viewport.x, u_viewport.y, uv.x);21 float py = mix(u_viewport.w, u_viewport.z, uv.y);22 float mr = (u_viewport.x + u_viewport.y) * 0.5;23 float mi = (u_viewport.z + u_viewport.w) * 0.5;24 float dr = px - mr, di = py - mi;25 float ca = cos(u_rotation), sa = sin(u_rotation);26 px = dr * ca - di * sa + mr;27 py = dr * sa + di * ca + mi;28 float zr = u_julia ? px : 0.0;29 float zi = u_julia ? py : 0.0;30 float cr = u_julia ? u_c.x : px;31 float ci = u_julia ? u_c.y : py;32 int iter = 0;33 for (int i = 0; i < 1000; i++) {34 if (i >= u_maxIter) break;35 if (zr * zr + zi * zi > 128.0) break;36 float tmp = zr * zr - zi * zi + cr;37 zi = 2.0 * zr * zi + ci;38 zr = tmp;39 iter++;40 }41 if (iter >= u_maxIter) {42 fragColor = vec4(u_primary, 1.0);43 } else {44 float sl = float(iter) - log2(log2(zr * zr + zi * zi)) + 4.0;45 float t = 0.5 + 0.5 * cos(3.0 + sl * 0.15 + u_time);46 fragColor = vec4(mix(u_primary, u_accent, t), 1.0);47 }48}49`;5051const UNIFORMS = ['resolution', 'viewport', 'c', 'julia', 'maxIter', 'primary', 'accent', 'rotation', 'time'];5253export function build(gl) {54 const make = (kind, source) => {55 const shader = gl.createShader(kind);56 gl.shaderSource(shader, source);57 gl.compileShader(shader);58 if (gl.getShaderParameter(shader, gl.COMPILE_STATUS)) return shader;59 gl.deleteShader(shader);60 return null;61 };62 const vs = make(gl.VERTEX_SHADER, VERT);63 const fs = make(gl.FRAGMENT_SHADER, FRAG);64 if (!vs || !fs) {65 gl.deleteShader(vs);66 gl.deleteShader(fs);67 return null;68 }69 const prog = gl.createProgram();70 gl.attachShader(prog, vs);71 gl.attachShader(prog, fs);72 gl.linkProgram(prog);73 if (!gl.getProgramParameter(prog, gl.LINK_STATUS)) {74 gl.deleteProgram(prog);75 gl.deleteShader(vs);76 gl.deleteShader(fs);77 return null;78 }79 gl.useProgram(prog);80 const vao = gl.createVertexArray();81 gl.bindVertexArray(vao);82 const buffer = gl.createBuffer();83 gl.bindBuffer(gl.ARRAY_BUFFER, buffer);84 gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([-1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, 1]), gl.STATIC_DRAW);85 const slot = gl.getAttribLocation(prog, 'a_position');86 gl.enableVertexAttribArray(slot);87 gl.vertexAttribPointer(slot, 2, gl.FLOAT, false, 0, 0);88 const loc = {};89 for (const name of UNIFORMS) loc[name] = gl.getUniformLocation(prog, `u_${name}`);90 return { prog, vs, fs, vao, buffer, loc };91}9293export function drop(gl, kit) {94 gl.deleteBuffer(kit.buffer);95 gl.deleteVertexArray(kit.vao);96 gl.deleteProgram(kit.prog);97 gl.deleteShader(kit.fs);98 gl.deleteShader(kit.vs);99}