diff --git a/assets/shaders/noise.frag b/assets/shaders/noise.frag index 6f89f42..27a6254 100644 --- a/assets/shaders/noise.frag +++ b/assets/shaders/noise.frag @@ -15,7 +15,8 @@ uniform float u_time; void main(){ vec2 st=(gl_FragCoord.xy+u_offset)/u_resolution; - + + vec2 q=vec2(0.); q.x=fbm(st); q.y=fbm(st+vec2(1.)); @@ -31,4 +32,5 @@ void main(){ vec3 color=mix(u_color_start,u_color_end,f*.3); gl_FragColor=vec4(color,1.); + } \ No newline at end of file diff --git a/assets/workers/ShaderWorker.ts b/assets/workers/ShaderWorker.ts index c0a4967..69f2f9c 100644 --- a/assets/workers/ShaderWorker.ts +++ b/assets/workers/ShaderWorker.ts @@ -6,9 +6,9 @@ import resolveLygia from "https://lygia.xyz/resolve.esm.js" declare const resolveLygia: (source: string) => string let interval: number +let timeout: number -const shaderOptions = { quality: 6, framerate: 1000 / 60 } -const bounds = { left: 0, bottom: 0 } +const shaderOptions = { quality: 1, framerate: 1000 / 60 } const screen = { width: 0, height: 0, devicePixelRatio: 0 } const _colorStart = [0, 0, 0] @@ -18,19 +18,22 @@ let fragmentShader = resolveLygia(noise); let canvas: OffscreenCanvas -async function main(canvas: OffscreenCanvas, fragmentShader2: string) { +let render: (time: number) => void + +let must_resize = false + +async function main(canvas: OffscreenCanvas) { 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.shaderSource(fragment, fragmentShader) gl.compileShader(fragment); if (!gl.getShaderParameter(fragment, gl.COMPILE_STATUS)) { const lastError = gl.getShaderInfoLog(fragment); @@ -87,6 +90,12 @@ async function main(canvas: OffscreenCanvas, fragmentShader2: string) { const gl2 = gl function render(time: number) { + if (must_resize) { + canvas.width = screen.width * shaderOptions.quality / 10 + canvas.height = screen.height * shaderOptions.quality / 10 + must_resize = false; + } + 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); @@ -110,11 +119,19 @@ async function main(canvas: OffscreenCanvas, fragmentShader2: string) { 0, // start at the beginning of the buffer ); + const secondRatio = screen.width / screen.height - const aspectRatio = Math.max(screen.width, screen.height) * screen.devicePixelRatio - gl2.uniform2f(offset, bounds.left, -bounds.bottom); + if (secondRatio < 1) { + const half = canvas.height / 2 + gl2.uniform2f(resolutionLocation, canvas.width, canvas.height * secondRatio); + gl2.uniform2f(offset, 0, -half + ((half) / (1 / secondRatio))); + } else { + const half = canvas.width / 2 + gl2.uniform2f(resolutionLocation, canvas.width / secondRatio, canvas.height); + gl2.uniform2f(offset, -half + ((half) / secondRatio), 0); + } 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); @@ -123,43 +140,71 @@ async function main(canvas: OffscreenCanvas, fragmentShader2: string) { 0, // offset 6, // num vertices to process ); - requestAnimationFrame(render) + if (timeout) { + self.clearTimeout(timeout) + timeout = self.setTimeout(() => { + requestAnimationFrame(render) + }, 1000 / shaderOptions.framerate) + } + console.log("a") } + // self.clearInterval(interval) - // interval = self.setInterval(() => requestAnimationFrame(render), shaderOptions.framerate) - requestAnimationFrame(render) + // interval = self.setInterval(() => requestAnimationFrame(render), 1000 / shaderOptions.framerate) + self.clearTimeout(timeout) + timeout = self.setTimeout(() => { + requestAnimationFrame(render) + }, 1000 / shaderOptions.framerate) + return render } self.addEventListener("message", (e) => { const data = e.data if (data.command == "initShader") { canvas = data.canvas - } else if (data.command == "killShader") { - console.log("killShader") - self.clearInterval(interval) - } else { - console.log(screen.devicePixelRatio) - bounds.bottom = data.bounds.bottom - bounds.left = data.bounds.left + shaderOptions.quality = data.shaderOptions.quality screen.devicePixelRatio = data.screen.devicePixelRatio screen.width = data.screen.width screen.height = data.screen.height + loadShader() + } else if (data.command == "killShader") { + console.log("killShader") + self.clearInterval(interval) + self.clearTimeout(timeout) + } + else if (data.command == "resumeShader") { + self.clearInterval(interval) + self.clearTimeout(timeout) + shaderOptions.framerate = data.shaderOptions.framerate + if (shaderOptions.framerate == 65) { + shaderOptions.framerate == Infinity + } + timeout = self.setTimeout(() => { + requestAnimationFrame(render) + }, 1000 / shaderOptions.framerate) + // interval = self.setInterval(() => requestAnimationFrame(render), 1000 / shaderOptions.framerate) + } else if (data.command == "recolorShader") { _colorStart[0] = data.colorStart[0] _colorStart[1] = data.colorStart[1] _colorStart[2] = data.colorStart[2] _colorEnd[0] = data.colorEnd[0] _colorEnd[1] = data.colorEnd[1] _colorEnd[2] = data.colorEnd[2] - if (data.command == "reloadShader") { - shaderOptions.quality = data.shaderOptions.quality - shaderOptions.framerate = data.shaderOptions.framerate - reloadShader() - } + } else if (data.command == "resizeShader") { + shaderOptions.quality = data.shaderOptions.quality + screen.devicePixelRatio = data.screen.devicePixelRatio + screen.width = data.screen.width + screen.height = data.screen.height + must_resize = true } }) -async function reloadShader() { +async function loadShader() { if (!canvas) { return } - let fragmentShader2 = fragmentShader.replace("#define FBM_OCTAVES 6", "#define FBM_OCTAVES " + shaderOptions.quality); - main(canvas, fragmentShader2); + console.log("a") + // let fragmentShader2 = fragmentShader.replace("#define FBM_OCTAVES 6", "#define FBM_OCTAVES " + shaderOptions.quality); + const temp = await main(canvas); + if (temp) { + render = temp + } } \ No newline at end of file diff --git a/components/AnimatedBackground.vue b/components/AnimatedBackground.vue index 3b57ee5..713000c 100644 --- a/components/AnimatedBackground.vue +++ b/components/AnimatedBackground.vue @@ -5,6 +5,7 @@ import ShaderWorker from '~/assets/workers/ShaderWorker?worker' const worker = new ShaderWorker() const canvas = ref(null) +const canvas_resize_temp = ref(null) export interface Props { colorStart?: [number, number, number] @@ -20,80 +21,80 @@ function initShader() { const offscreen = canvas.value?.transferControlToOffscreen() worker.postMessage({ command: "initShader", - canvas: offscreen + canvas: offscreen, + screen: { height: window.innerHeight * devicePixelRatio, width: window.innerWidth * devicePixelRatio, devicePixelRatio }, + shaderOptions: { quality: settingsStore.shaderQuality, framerate: settingsStore.shaderFramerate }, }, [offscreen]) -} -function safeReloadShader() { - reloadShader() + if (settingsStore.shaderFramerate == 0) { + killShader() + } + resizeShader() } -function reloadShader() { - const bounds = canvas.value?.getBoundingClientRect() - if (!bounds) { return } +function recolorShader() { worker.postMessage({ - command: 'reloadShader', - quality: settingsStore.shaderQuality, - framerate: settingsStore.shaderFramerate, - bounds: { left: bounds.left, bottom: bounds.bottom }, - screen: { height: window.screen.height, width: window.screen.width, devicePixelRatio }, - shaderOptions: { quality: settingsStore.shaderQuality, framerate: settingsStore.shaderFramerate }, + command: 'recolorShader', colorStart: props.colorStart, colorEnd: props.colorEnd }) } function resizeShader() { - const bounds = canvas.value?.getBoundingClientRect() - if (!bounds) { return } worker.postMessage({ - command: 'changeBounds', - bounds: { left: bounds.left, bottom: bounds.bottom }, - screen: { height: window.screen.height, width: window.screen.width, devicePixelRatio }, - colorStart: props.colorStart, - colorEnd: props.colorEnd + command: 'resizeShader', + screen: { height: window.innerHeight * devicePixelRatio, width: window.innerWidth * devicePixelRatio, devicePixelRatio }, + shaderOptions: { quality: settingsStore.shaderQuality, framerate: settingsStore.shaderFramerate }, }) } -function endShader() { +function killShader() { worker.postMessage({ command: 'killShader' }) } -watch(() => [settingsStore.shaderQuality, settingsStore.shaderFramerate], () => { - safeReloadShader() +function resumeShader() { + worker.postMessage({ + command: 'resumeShader', + shaderOptions: { quality: settingsStore.shaderQuality, framerate: settingsStore.shaderFramerate }, + }) +} + +watch(() => [settingsStore.shaderFramerate], () => { + if (settingsStore.shaderFramerate == 0) { + killShader() + return + } + resumeShader() + }) +watch(() => [settingsStore.shaderQuality], () => { + resizeShader() +}) -let has_resized = false watch(() => [props.colorEnd, props.colorStart], () => { - if (!has_resized) { - has_resized = true - nextTick(() => { - resizeShader() - has_resized = false - }) - } + recolorShader() }) onMounted(async () => { nextTick(() => { + recolorShader() initShader() window.addEventListener("resize", resizeShader, true) - safeReloadShader() }) - }) onUnmounted(() => { window.removeEventListener("resize", resizeShader) - endShader() + killShader() }) \ No newline at end of file diff --git a/components/OptionsDialog.vue b/components/OptionsDialog.vue index 2aa4181..eaab71f 100644 --- a/components/OptionsDialog.vue +++ b/components/OptionsDialog.vue @@ -6,6 +6,9 @@ export interface Props { isVisible: boolean; } +const _devicePixelRatio = computed(() => devicePixelRatio) +const _window = computed(() => window) + const props = withDefaults(defineProps(), { }) @@ -15,10 +18,6 @@ const close = () => { emit('close') } -watch(() => settingsStore.shaderQuality, async () => { - console.log(settingsStore.shaderQuality) -}) -