From fd711496c59a4e2c42e7021ed74515292fe22f3d Mon Sep 17 00:00:00 2001 From: Warinyourself Date: Sun, 21 Jun 2026 04:40:06 +0300 Subject: [PATCH] Fix color input: pass string value directly instead of reading .hex --- .../base/settings/SettingsCustom.tsx | 7 +- src/components/themes/tunnel/fragment.glsl | 43 +++++++++++++ src/components/themes/tunnel/index.tsx | 64 +++++++++++++++++++ src/components/themes/tunnel/vertex.glsl | 5 ++ 4 files changed, 115 insertions(+), 4 deletions(-) create mode 100644 src/components/themes/tunnel/fragment.glsl create mode 100644 src/components/themes/tunnel/index.tsx create mode 100644 src/components/themes/tunnel/vertex.glsl diff --git a/src/components/base/settings/SettingsCustom.tsx b/src/components/base/settings/SettingsCustom.tsx index 32da93a..aa0c718 100644 --- a/src/components/base/settings/SettingsCustom.tsx +++ b/src/components/base/settings/SettingsCustom.tsx @@ -9,7 +9,6 @@ import AppColorSelector from '@/components/app/AppColorSelector' import AppPaletteSelector from '@/components/app/AppPaletteSelector' import type { AppInputButton, - AppInputColor, AppInputThemeGeneral, AppInputThemePalette, AppInputThemeSelector, @@ -62,9 +61,9 @@ export default defineComponent({ class={options?.class} label={label} modelValue={value as string} - onUpdate:modelValue={(color: AppInputColor) => { - themeStore.changeSettingsThemeInput({ key: input.name, value: color.hex }) - input.callback?.(color.hex) + onUpdate:modelValue={(color: string) => { + themeStore.changeSettingsThemeInput({ key: input.name, value: color }) + input.callback?.(color) }} /> ) diff --git a/src/components/themes/tunnel/fragment.glsl b/src/components/themes/tunnel/fragment.glsl new file mode 100644 index 0000000..ef418f0 --- /dev/null +++ b/src/components/themes/tunnel/fragment.glsl @@ -0,0 +1,43 @@ +// Original: http://www.pouet.net/prod.php?which=57245 +// Credits: Danilo Guanabara +#ifdef GL_ES +precision highp float; +#endif + +uniform float uTime; +uniform vec2 uResolution; + +// Per-iteration z offset — controls how far apart the RGB channels are in time. +// Higher values spread colors into wider rainbow bands; lower values keep it monochromatic. +uniform float uStep; + +// Frequency of the radial sine wave — controls ring/wave density. +// Higher values produce tighter, more numerous rings; lower values produce broad sweeping waves. +uniform float uFrequency; + +// Tunnel pull strength — scales the UV distortion toward the center. +// 0.0 = flat pattern with no perspective; higher values create a deeper vortex effect. +uniform float uAmplitude; + +// Color channel intensity — inversely controls brightness (smaller = brighter). +// Too high darkens the image; too low causes overexposure / color clipping. +uniform float uBrightness; + +#define t uTime +#define r uResolution + +void main() { + vec3 c; + float l, z = t; + for (int i = 0; i < 3; i++) { + vec2 uv, p = gl_FragCoord.xy / r; + uv = p; + p -= .5; + p.x *= r.x / r.y; + z += uStep; + l = length(p); + uv += p / l * (sin(z) + 1.) * uAmplitude * abs(sin(l * uFrequency - z - z)); + c[i] = uBrightness / length(mod(uv, 1.) - .5); + } + gl_FragColor = vec4(c / l, 1.0); +} diff --git a/src/components/themes/tunnel/index.tsx b/src/components/themes/tunnel/index.tsx new file mode 100644 index 0000000..a6c01c4 --- /dev/null +++ b/src/components/themes/tunnel/index.tsx @@ -0,0 +1,64 @@ +import { defineComponent, ref, computed, onMounted, onBeforeUnmount } from 'vue' +import GL from '@/utils/gl' +import { useThemeStore } from '@/store/theme' +import fragmentShader from './fragment.glsl' +import vertexShader from './vertex.glsl' + +let render: GL + +export default defineComponent({ + name: 'TunnelTheme', + setup() { + const themeStore = useThemeStore() + const canvasRef = ref(null) + + const animationSpeed = computed(() => themeStore.getThemeInput('animation-speed')?.value as number || 5) + const pxratio = computed(() => themeStore.getThemeInput('pxratio')?.value as number || 0.8) + const step = computed(() => themeStore.getThemeInput('step')?.value as number || 0.07) + const frequency = computed(() => themeStore.getThemeInput('frequency')?.value as number || 9.0) + const amplitude = computed(() => themeStore.getThemeInput('amplitude')?.value as number || 1.0) + const brightness = computed(() => themeStore.getThemeInput('brightness')?.value as number || 0.01) + + onMounted(() => { + render = new GL( + canvasRef.value!, + vertexShader, + fragmentShader, + window.innerWidth, + window.innerHeight, + { + renderOptions: { externalTimeUse: true }, + renderHook() { + const gl = this as unknown as GL + + if (!gl.programInfo.uniforms.step) { + gl.programInfo.uniforms.step = gl.ctx.getUniformLocation(gl.program as WebGLProgram, 'uStep') + gl.programInfo.uniforms.frequency = gl.ctx.getUniformLocation(gl.program as WebGLProgram, 'uFrequency') + gl.programInfo.uniforms.amplitude = gl.ctx.getUniformLocation(gl.program as WebGLProgram, 'uAmplitude') + gl.programInfo.uniforms.brightness = gl.ctx.getUniformLocation(gl.program as WebGLProgram, 'uBrightness') + } + + gl.pxratio = pxratio.value + gl.time += animationSpeed.value / 500 + gl.ctx.uniform1f(gl.programInfo.uniforms.time, gl.time) + gl.ctx.uniform1f(gl.programInfo.uniforms.step, step.value) + gl.ctx.uniform1f(gl.programInfo.uniforms.frequency, frequency.value) + gl.ctx.uniform1f(gl.programInfo.uniforms.amplitude, amplitude.value) + gl.ctx.uniform1f(gl.programInfo.uniforms.brightness, brightness.value) + } + } + ) + render.running = true + }) + + onBeforeUnmount(() => { + render.running = false + }) + + return () => ( +
+ +
+ ) + } +}) diff --git a/src/components/themes/tunnel/vertex.glsl b/src/components/themes/tunnel/vertex.glsl new file mode 100644 index 0000000..9d68485 --- /dev/null +++ b/src/components/themes/tunnel/vertex.glsl @@ -0,0 +1,5 @@ +attribute vec2 aVertexPosition; + +void main() { + gl_Position = vec4(aVertexPosition, 0.0, 1.0); +}