WIP : shader Worker

This commit is contained in:
2024-05-22 10:32:42 +02:00
parent 6f7b1bdf03
commit ca87817b33
3 changed files with 211 additions and 125 deletions
+156
View File
@@ -0,0 +1,156 @@
import noise from "~/assets/shaders/noise.frag?raw"
// @ts-ignore Import module
import resolveLygia from "https://lygia.xyz/resolve.esm.js"
declare const resolveLygia: (source: string) => string
let interval: number
const shaderOptions = { quality: 6, framerate: 1000 / 60 }
const bounds = { left: 0, bottom: 0 }
const screen = { width: 0, height: 0, devicePixelRatio: 0 }
let fragmentShader = resolveLygia(noise);
let canvas: OffscreenCanvas
async function main(canvas: OffscreenCanvas, fragmentShader2: string, _colorStart: [number, number, number], _colorEnd: [number, number, number]) {
const gl = canvas.getContext("webgl");
if (!gl) {
return;
}
// setup GLSL program
const program = gl.createProgram()
if (!program) { return }
const fragment = gl.createShader(gl.FRAGMENT_SHADER);
if (!fragment) { return }
gl.shaderSource(fragment, fragmentShader2)
gl.compileShader(fragment);
if (!gl.getShaderParameter(fragment, gl.COMPILE_STATUS)) {
const lastError = gl.getShaderInfoLog(fragment);
console.error(lastError)
gl.deleteShader(fragment);
return
}
const vertex = gl.createShader(gl.VERTEX_SHADER);
if (!vertex) { return }
gl.shaderSource(vertex, `attribute vec4 a_position;void main() {gl_Position = a_position;}`)
gl.compileShader(vertex);
if (!gl.getShaderParameter(vertex, gl.COMPILE_STATUS)) {
const lastError = gl.getShaderInfoLog(vertex);
console.error(lastError)
gl.deleteShader(vertex);
return
}
gl.attachShader(program, fragment)
gl.attachShader(program, vertex)
gl.linkProgram(program)
const linked = gl.getProgramParameter(program, gl.LINK_STATUS);
if (!linked) { return }
// const program = webglUtils.createProgramFromSources(gl, [vs, fragmentShader2]);
// look up where the vertex data needs to go.
const positionAttributeLocation = gl.getAttribLocation(program, "a_position");
// look up uniform locations
const offset = gl.getUniformLocation(program, "u_offset");
const resolutionLocation = gl.getUniformLocation(program, "u_resolution");
const colorStart = gl.getUniformLocation(program, "u_color_start");
const quality = gl.getUniformLocation(program, "u_quality");
const colorEnd = gl.getUniformLocation(program, "u_color_end");
const timeLocation = gl.getUniformLocation(program, "u_time");
// Create a buffer to put three 2d clip space points in
const positionBuffer = gl.createBuffer();
// Bind it to ARRAY_BUFFER (think of it as ARRAY_BUFFER = positionBuffer)
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
// fill it with a 2 triangles that cover clipspace
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
-1, -1, // first triangle
1, -1,
-1, 1,
-1, 1, // second triangle
1, -1,
1, 1,
]), gl.STATIC_DRAW);
const gl2 = gl
function render(time: number) {
time *= 0.001; // convert to seconds
// Tell WebGL how to convert from clip space to pixels
gl2.viewport(0, 0, gl2.canvas.width, gl2.canvas.height);
// Tell it to use our program (pair of shaders)
gl2.useProgram(program);
// Turn on the attribute
gl2.enableVertexAttribArray(positionAttributeLocation);
// Bind the position buffer.
gl2.bindBuffer(gl2.ARRAY_BUFFER, positionBuffer);
// Tell the attribute how to get data out of positionBuffer (ARRAY_BUFFER)
gl2.vertexAttribPointer(
positionAttributeLocation,
2, // 2 components per iteration
gl2.FLOAT, // the data is 32bit floats
false, // don't normalize the data
0, // 0 = move forward size * sizeof(type) each iteration to get the next position
0, // start at the beginning of the buffer
);
const aspectRatio = Math.max(screen.width, screen.height) * screen.devicePixelRatio
gl2.uniform2f(offset, bounds.left, -bounds.bottom);
// console.log(canvas.getBoundingClientRect().left * window.devicePixelRatio)
gl2.uniform1i(quality, shaderOptions.quality)
gl2.uniform2f(resolutionLocation, aspectRatio, aspectRatio);
gl2.uniform3f(colorStart, _colorStart[0], _colorStart[1], _colorStart[2]);
gl2.uniform3f(colorEnd, _colorEnd[0], _colorEnd[1], _colorEnd[2]);
gl2.uniform1f(timeLocation, time);
gl2.drawArrays(
gl2.TRIANGLES,
0, // offset
6, // num vertices to process
);
}
self.clearInterval(interval)
interval = self.setInterval(() => requestAnimationFrame(render), shaderOptions.framerate)
}
self.addEventListener("message", (e) => {
const data = e.data
if (data.command == "initShader") {
canvas = data.canvas
} else if (data.command == "killShader") {
self.clearInterval(interval)
} else {
bounds.bottom = data.bounds.bottom
bounds.left = data.bounds.left
screen.devicePixelRatio = data.screen.devicePixelRatio
screen.width = data.screen.width
screen.height = data.screen.height
if (data.command == "reloadShader") {
shaderOptions.quality = data.shaderOptions.quality
shaderOptions.framerate = data.shaderOptions.framerate
reloadShader(data.canvas, data.colorStart, data.colorEnd)
}
}
})
async function reloadShader(canvas: OffscreenCanvas, _colorStart: [number, number, number], _colorEnd: [number, number, number]) {
if (!canvas) { return }
let fragmentShader2 = fragmentShader.replace("#define FBM_OCTAVES 6", "#define FBM_OCTAVES " + shaderOptions.quality);
self.clearInterval(interval)
main(canvas, fragmentShader2, _colorStart, _colorEnd);
}