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}