out.vert

#version 300 es

layout (location = 0) in vec3 aVertexPosition;
layout (location = 1) in vec4 aVertexColor;

uniform float uTime;
uniform vec2 uMouse;

out vec3 vVertexPosition;
out vec4 vVertexColor;

void main(){
  vec2 p = uMouse + aVertexPosition.xy;
  float z = cos(length(p * 20.0) - uTime) * 0.1;
  
  vVertexPosition = aVertexPosition;
  vVertexPosition.z = z;
  vVertexColor = aVertexColor;
}

out.frag

#version 300 es

precision highp float;

out vec4 fragColor;

void main(){
  fragColor = vec4(1.0);
}

in.vert

#version 300 es

layout (location = 0) in vec3 aVertexPosition;
layout (location = 1) in vec4 aVertexColor;

uniform mat4 uMatrix;

out vec4 vVertexColor;

void main(){
  vVertexColor = aVertexColor;
  gl_Position = uMatrix * vec4(aVertexPosition, 1.0);
  gl_PointSize = 1.0;
}

in.frag

#version 300 es

precision highp float;

in vec4 vVertexColor;

out vec4 fragColor;

void main(){
  fragColor = vVertexColor;
}

render.ts

import { SketchGl, type SketchConfig, type SketchFn } from "sketchgl"
import { Uniforms } from "sketchgl/program"
import { ImageCanvas } from "sketchgl/texture"
import { ImageInterleavedData } from "sketchgl/utility"
import { Timer } from "sketchgl/interactive"
import { SwapTFRenderer } from "sketchgl/renderer"
import { WavePlaneCamera } from "@/lib/feature/particle/wave-plane"

import vertForOut from "./out.vert?raw"
import fragForOut from "./out.frag?raw"

import vertForIn from "./in.vert?raw"
import fragForIn from "./in.frag?raw"

import image from "@/assets/542x542/autumn-leaves_00037.png"

const sketch: SketchFn = ({ gl, canvas }) => {
  const uniformsFor = {
    update: new Uniforms(gl, ["uTime", "uMouse"]),
    render: new Uniforms(gl, ["uMatrix"])
  }

  const imgCvs = new ImageCanvas(image, 542, 542)
  const interleave = new ImageInterleavedData(imgCvs, ["vVertexPosition", "vVertexColor"])

  interleave.add("vVertexPosition", {
    components: 3,
    generate: ({ position }) => {
      const { x, y } = position
      return [x, -y, 0]
    }
  })
  interleave.useImageColorAs("vVertexColor")

  const renderer = new SwapTFRenderer(gl, interleave.keys)

  renderer.attachUpdateProgram(vertForOut, fragForOut)
  renderer.attachRenderProgram(vertForIn, fragForIn)

  renderer.registUpdateAttrib("vVertexPosition", { location: 0, components: 3 })
  renderer.registUpdateAttrib("vVertexColor", { location: 1, components: 4 })

  renderer.registRenderAttrib("aVertexPosition", { location: 0, components: 3 })
  renderer.registRenderAttrib("aVertexColor", { location: 1, components: 4 })

  uniformsFor.update.init(renderer.glProgramForUpdate)
  uniformsFor.render.init(renderer.glProgramForRender)

  // TODO: ライブラリでうまいこと提供する
  const camera = new WavePlaneCamera(canvas, 3)

  const timer = new Timer()
  timer.start()

  gl.clearColor(0.0, 0.0, 0.0, 1.0)
  gl.clearDepth(1.0)

  gl.disable(gl.DEPTH_TEST)
  gl.disable(gl.CULL_FACE)
  gl.enable(gl.BLEND)
  gl.blendFuncSeparate(gl.SRC_ALPHA, gl.ONE, gl.ONE, gl.ONE)
  gl.disable(gl.RASTERIZER_DISCARD)

  return {
    preload: [imgCvs.load()],

    preloaded: [
      () => {
        const interleavedArray = interleave.generate()
        renderer.setup(new Float32Array(interleavedArray))
      }
    ],

    drawOnFrame() {
      camera.update()

      renderer.startUpdate()

      uniformsFor.update.float("uTime", timer.elapsed * 0.001)
      uniformsFor.update.fvector2("uMouse", camera.mouseCoords)

      gl.drawArrays(gl.POINTS, 0, imgCvs.width * imgCvs.height)

      renderer.endUpdate()

      gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)
      gl.viewport(0, 0, gl.canvas.width, gl.canvas.height)

      renderer.startRender()

      uniformsFor.render.fmatrix4("uMatrix", camera.matrix.values)

      gl.drawArrays(gl.POINTS, 0, imgCvs.width * imgCvs.height)

      renderer.endRender()
    }
  }
}

export const onload = () => {
  const config: SketchConfig = {
    canvas: {
      el: "gl-canvas",
      fit: "square",
      autoResize: true
    }
  }
  SketchGl.init(config, sketch)
}