@thi.ng/webgl-shadertoy
Version: 
Basic WebGL scaffolding for running interactive fragment shaders via @thi.ng/shader-ast
104 lines (103 loc) • 2.77 kB
JavaScript
import { F, I, V2, V4 } from "@thi.ng/shader-ast/api/types";
import { assign } from "@thi.ng/shader-ast/ast/assign";
import { defMain, defn } from "@thi.ng/shader-ast/ast/function";
import { FLOAT0, FLOAT1, vec4 } from "@thi.ng/shader-ast/ast/lit";
import { compileModel } from "@thi.ng/webgl/buffer";
import { draw } from "@thi.ng/webgl/draw";
import { defQuadModel } from "@thi.ng/webgl/geo/quad";
import { defShader } from "@thi.ng/webgl/shader";
const shaderToy = (opts) => {
  const gl = opts.gl;
  const model = defQuadModel({ uv: false });
  model.textures = opts.textures || [];
  compileModel(gl, model);
  opts.canvas.addEventListener("mousemove", (e) => {
    const rect = opts.canvas.getBoundingClientRect();
    const dpr = window.devicePixelRatio;
    model.uniforms.mouse = [
      (e.clientX - rect.left) * dpr,
      (rect.height - (e.clientY - rect.top)) * dpr
    ];
  });
  opts.canvas.addEventListener("mousedown", (e) => {
    model.uniforms.mouseButtons = e.buttons;
  });
  opts.canvas.addEventListener("mouseup", (e) => {
    model.uniforms.mouseButtons = e.buttons;
  });
  let active;
  let t0;
  const update = (time) => {
    const w = gl.drawingBufferWidth;
    const h = gl.drawingBufferHeight;
    model.uniforms.time = time;
    model.uniforms.resolution = [w, h];
    gl.viewport(0, 0, w, h);
    draw(model);
  };
  const updateRAF = () => {
    update((Date.now() - t0) * 1e-3);
    active && requestAnimationFrame(updateRAF);
  };
  const instance = {
    start() {
      t0 = Date.now();
      active = true;
      requestAnimationFrame(updateRAF);
    },
    stop() {
      active = false;
    },
    update(time) {
      update(time);
    },
    recompile(main, shaderOpts) {
      if (model.shader) {
        model.shader.release();
      }
      model.shader = defShader(
        gl,
        {
          vs: (gl2, _, ins) => [
            defMain(() => [
              assign(
                gl2.gl_Position,
                vec4(ins.position, FLOAT0, FLOAT1)
              )
            ])
          ],
          fs: (gl2, unis, _, outputs) => [
            defMain(() => [
              assign(
                outputs.fragColor,
                defn(
                  V4,
                  "mainImage",
                  [],
                  () => main(gl2, unis)
                )()
              )
            ])
          ],
          attribs: {
            position: V2
          },
          uniforms: {
            resolution: V2,
            mouse: [V2, [0, 0]],
            mouseButtons: [I, 0],
            time: F,
            ...opts.uniforms
          }
        },
        shaderOpts || opts.opts
      );
    },
    model
  };
  instance.recompile(opts.main);
  return instance;
};
export {
  shaderToy
};