UNPKG

@thi.ng/webgl-shadertoy

Version:

Basic WebGL scaffolding for running interactive fragment shaders via @thi.ng/shader-ast

107 lines (106 loc) 2.82 kB
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"; class ShaderToy { constructor(opts) { this.opts = opts; this.model = defQuadModel({ uv: false }); this.model.textures = opts.textures || []; compileModel(opts.gl, this.model); opts.canvas.addEventListener("mousemove", (e) => { const rect = opts.canvas.getBoundingClientRect(); const dpr = window.devicePixelRatio; this.model.uniforms.mouse = [ (e.clientX - rect.left) * dpr, (rect.height - (e.clientY - rect.top)) * dpr ]; }); opts.canvas.addEventListener("mousedown", (e) => { this.model.uniforms.mouseButtons = e.buttons; }); opts.canvas.addEventListener("mouseup", (e) => { this.model.uniforms.mouseButtons = e.buttons; }); this.recompile(opts.main); } model; t0; active = false; start() { this.t0 = Date.now(); this.active = true; const update = () => { this.update((Date.now() - this.t0) * 1e-3); this.active && requestAnimationFrame(update); }; requestAnimationFrame(update); } stop() { this.active = false; } update(time) { const { opts: { gl }, model } = this; 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); } recompile(main, shaderOpts) { const { opts, model } = this; if (model.shader) { model.shader.release(); } model.shader = defShader( opts.gl, { vs: (gl, _, ins) => [ defMain(() => [ assign( gl.gl_Position, vec4(ins.position, FLOAT0, FLOAT1) ) ]) ], fs: (gl, unis, _, outputs) => [ defMain(() => [ assign( outputs.fragColor, defn( V4, "mainImage", [], () => main(gl, unis) )() ) ]) ], attribs: { position: V2 }, uniforms: { resolution: V2, mouse: [V2, [0, 0]], mouseButtons: [I, 0], time: F, ...opts.uniforms } }, shaderOpts || opts.opts ); } } const shaderToy = (opts) => new ShaderToy(opts); export { ShaderToy, shaderToy };