diff --git a/package-lock.json b/package-lock.json index 0a508b0..c18c854 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,6 +11,7 @@ "@lucide/vue": "^1.17.0", "@vueuse/core": "^14.3.0", "pinia": "^3.0.4", + "vividgl": "^0.1.0", "vue": "^3.5.32", "vue-i18n": "^11.4.5", "vue-router": "^5.0.4" @@ -3690,6 +3691,12 @@ "vue": ">=3.2.13" } }, + "node_modules/vividgl": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/vividgl/-/vividgl-0.1.0.tgz", + "integrity": "sha512-JADDWl6pgKm8h3LSB1N5cK48MCVjWwTd+l4VIln7S1lO7rPT7+mCNv7OSVxdb2LPhlJWAzTFhubw8AniJWJ7cA==", + "license": "MIT" + }, "node_modules/vscode-uri": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.1.0.tgz", diff --git a/package.json b/package.json index 53c259a..59698a3 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ "@lucide/vue": "^1.17.0", "@vueuse/core": "^14.3.0", "pinia": "^3.0.4", + "vividgl": "^0.1.0", "vue": "^3.5.32", "vue-i18n": "^11.4.5", "vue-router": "^5.0.4" diff --git a/src/components/base/BackgroundImage.tsx b/src/components/base/BackgroundImage.tsx index 5dccb81..ad1eb0f 100644 --- a/src/components/base/BackgroundImage.tsx +++ b/src/components/base/BackgroundImage.tsx @@ -1,47 +1,89 @@ -import { defineComponent, computed, h } from 'vue' +import { defineComponent, ref, watch, onMounted, onBeforeUnmount } from 'vue' +import { VividGL } from 'vividgl' +import type { ParamValues } from 'vividgl' import { useThemeStore } from '@/store/theme' +import type { AppInputTheme } from '@/models/app' -import flow from '@/components/themes/flow' -import sphere from '@/components/themes/sphere' -import rings from '@/components/themes/rings' -import plasma from '@/components/themes/plasma' -import destruction from '@/components/themes/destruction' -import tenderness from '@/components/themes/tenderness' -import planet from '@/components/themes/planet' -import random from '@/components/themes/random' -import tunnel from '@/components/themes/tunnel' -import contour from '@/components/themes/contour' -import rings3d from '@/components/themes/rings3d' -import zappy from '@/components/themes/zappy' - -const themeMap: Record = { - flow, - sphere, - rings, - plasma, - destruction, - tenderness, - planet, - random, - tunnel, - contour, - rings3d, - zappy, +// Maps osmos setting names (kebab-case) → VividGL param names (camelCase) +// Auto-converts kebab to camelCase; explicit overrides for renames +const PARAM_MAP: Record = { + 'animation-speed': 'speed', + 'color-glow': 'glowColor', + 'color-fog': 'fogColor', +} + +function toVividParam(name: string): string { + return PARAM_MAP[name] ?? name.replace(/-([a-z])/g, (_, c) => c.toUpperCase()) +} + +function buildParams(settings: AppInputTheme[] | undefined): ParamValues { + const params: ParamValues = {} + for (const input of settings ?? []) { + if (input.type === 'button') continue + const key = toVividParam(input.name) + if (key !== 'pxratio' && !Array.isArray(input.value)) + params[key] = input.value as string | number | boolean + } + return params +} + +function getPxratio(settings: AppInputTheme[] | undefined): number { + return (settings?.find(i => i.name === 'pxratio')?.value as number) ?? 0.8 } export default defineComponent({ name: 'BackgroundImage', setup() { const themeStore = useThemeStore() - const theme = computed(() => themeStore.activeTheme) - - return () => { - const component = themeMap[theme.value.component ?? 'random'] ?? themeMap.random - return ( -
- {h(component)} -
- ) + const canvasRef = ref(null) + let bg: VividGL | null = null + + const init = (themeName: string) => { + bg?.destroy() + bg = null + if (!canvasRef.value) return + + const knownThemes = VividGL.themes() + const name = knownThemes.includes(themeName) ? themeName : knownThemes[0]! + const params = buildParams(themeStore.activeTheme.settings) + + try { + bg = new VividGL(canvasRef.value, name, params) + bg.pxratio = getPxratio(themeStore.activeTheme.settings) + bg.start() + } catch (e) { + console.warn('[BackgroundImage] VividGL error:', e) + } } + + onMounted(() => init(themeStore.activeTheme.component ?? 'random')) + + // Switch theme when selection changes + watch(() => themeStore.currentTheme, () => { + init(themeStore.activeTheme.component ?? 'random') + }) + + // Sync individual param updates in real time + watch( + () => themeStore.activeTheme.settings, + (settings) => { + if (!bg) return + for (const input of settings ?? []) { + if (input.type === 'button') continue + if (input.name === 'pxratio') { bg.pxratio = input.value as number; continue } + if (!Array.isArray(input.value)) + bg.setParam(toVividParam(input.name), input.value as string | number | boolean) + } + }, + { deep: true } + ) + + onBeforeUnmount(() => bg?.destroy()) + + return () => ( +
+ +
+ ) } }) diff --git a/src/components/themes/contour/fragment.glsl b/src/components/themes/contour/fragment.glsl deleted file mode 100644 index 44e5af0..0000000 --- a/src/components/themes/contour/fragment.glsl +++ /dev/null @@ -1,117 +0,0 @@ -#version 300 es -#ifdef GL_ES -precision highp float; -#endif - -uniform float uTime; -uniform vec2 uResolution; - -// Zoom/scale of the noise pattern — higher values zoom in, revealing more detail -uniform float uScale; - -// Number of stripes/contour lines across the noise field -uniform float uScaleContour; - -// Line width: controls how much of the contour field is visible (0 = thin hairlines, 1 = wide bands) -uniform float uWidth; - - -// Max line band: caps the upper visible threshold so all lines have similar spatial width -// smaller = more uniform thin lines, larger = allows wide bands -uniform float uMaxLine; - -// First gradient color (RGB) -uniform vec3 uColor1; - -// Second gradient color (RGB) -uniform vec3 uColor2; - -// Background color — shown where lines and glow are absent -uniform vec3 uBackground; - -const float M_PI = 3.14159265; -const int NUM_OCTAVES = 2; - -// Hash functions — map domain to pseudo-random [0, 1) -float hash11(float t) { - return fract(sin(t * 56789.0) * 56789.0); -} - -float hash21(vec2 uv) { - return hash11(hash11(uv[0]) + 2.0 * hash11(uv[1])); -} - -// Returns a unit-length gradient vector for a given grid cell -vec2 hashGradient2(vec2 uv) { - float t = hash21(uv); - return vec2(cos(2.0 * M_PI * t), sin(2.0 * M_PI * t)); -} - -// Bilinear mix helper -float mix2(float f00, float f10, float f01, float f11, vec2 uv) { - return mix(mix(f00, f10, uv[0]), mix(f01, f11, uv[0]), uv[1]); -} - -// Rotate a 2D vector by angle r (radians) -vec2 rotate2(vec2 uv, float r) { - mat2 R = mat2(cos(r), sin(r), -sin(r), cos(r)); - return R * uv; -} - -// Gradient (Perlin) noise with optional gradient rotation for animation -float gradientNoise(vec2 uv, float r) { - vec2 uvi = floor(uv); - vec2 uvf = uv - uvi; - vec2 g00 = rotate2(hashGradient2(uvi + vec2(0.0, 0.0)), r); - vec2 g10 = rotate2(hashGradient2(uvi + vec2(1.0, 0.0)), r); - vec2 g01 = rotate2(hashGradient2(uvi + vec2(0.0, 1.0)), r); - vec2 g11 = rotate2(hashGradient2(uvi + vec2(1.0, 1.0)), r); - float f00 = dot(g00, uvf - vec2(0.0, 0.0)); - float f10 = dot(g10, uvf - vec2(1.0, 0.0)); - float f01 = dot(g01, uvf - vec2(0.0, 1.0)); - float f11 = dot(g11, uvf - vec2(1.0, 1.0)); - float t = mix2(f00, f10, f01, f11, smoothstep(vec2(0.0), vec2(1.0), uvf)); - // Normalize: theoretical bounds are +-1/sqrt(2) ≈ +-0.7 - return (t / 0.7 + 1.0) * 0.5; -} - -// Fractional Brownian Motion — stacks octaves for richer noise detail -float noise(vec2 uv, float r) { - float result = 0.0; - for (int i = 0; i < NUM_OCTAVES; i++) { - float p = pow(2.0, float(i)); - result += gradientNoise(uv * p, r) / p; - } - // Normalize result back to [0, 1] - result /= (pow(2.0, float(NUM_OCTAVES)) - 1.0) / pow(2.0, float(NUM_OCTAVES - 1)); - return result; -} - -// Maps noise value to a periodic wave that creates the contour bands -float wave(float t) { - return 0.5 * (1.0 - cos(uScaleContour * M_PI * t)); -} - -out vec4 fragColor; -void main() { - vec2 uv = uScale * gl_FragCoord.xy / uResolution.y; - - // Animate by rotating noise gradients over time (0.628 ≈ 0.1 * 2π) - float r = 0.628 * uTime; - - float noise_fac = noise(uv, r); - float contour_fac = wave(noise_fac); - vec3 color = mix(uColor1, uColor2, noise_fac); - - float lo = 1.0 - uWidth; - float hi = lo + uMaxLine; - float fw = fwidth(contour_fac); - - // Crisp anti-aliased line: only pixels in the [lo, hi] band are visible, - // bounding maximum spatial width so all lines appear roughly equal-sized - float line = smoothstep(lo - fw, lo + fw, contour_fac) - - smoothstep(hi - fw, hi + fw, contour_fac); - - float brightness = line; - fragColor = vec4(mix(uBackground, color, brightness), 1.0); -} diff --git a/src/components/themes/contour/index.tsx b/src/components/themes/contour/index.tsx deleted file mode 100644 index c54c776..0000000 --- a/src/components/themes/contour/index.tsx +++ /dev/null @@ -1,86 +0,0 @@ -import { defineComponent, ref, computed, onMounted, onBeforeUnmount } from 'vue' -import GL from '@/utils/gl' -import PostProcessGL from '@/utils/postprocess' -import { useThemeStore } from '@/store/theme' -import { hexToRgb } from '@/utils/color' -import fragmentShader from './fragment.glsl' -import vertexShader from './vertex.glsl' - -let render: PostProcessGL - -export default defineComponent({ - name: 'ContourTheme', - setup() { - const themeStore = useThemeStore() - const canvasRef = ref(null) - - const animationSpeed = computed(() => themeStore.getThemeInput('animation-speed')?.value as number || 0.7) - const pxratio = computed(() => themeStore.getThemeInput('pxratio')?.value as number || 0.8) - const scale = computed(() => themeStore.getThemeInput('scale')?.value as number || 3.0) - const contour = computed(() => themeStore.getThemeInput('contour')?.value as number || 32.0) - const width = computed(() => themeStore.getThemeInput('width')?.value as number || 0.7) - - const maxLine = computed(() => themeStore.getThemeInput('max-line')?.value as number || 0.3) - const color1 = computed(() => themeStore.getThemeInput('color-active')?.value as string || '#FF00FF') - const color2 = computed(() => themeStore.getThemeInput('color-second')?.value as string || '#00FFFF') - const background = computed(() => themeStore.getThemeInput('color-bg')?.value as string || '#000000') - - onMounted(() => { - render = new PostProcessGL( - canvasRef.value!, - vertexShader, - fragmentShader, - window.innerWidth, - window.innerHeight, - { - renderOptions: { externalTimeUse: true }, - renderHook() { - const gl = this as unknown as GL - - if (!gl.programInfo.uniforms.scale) { - gl.programInfo.uniforms.scale = gl.ctx.getUniformLocation(gl.program as WebGLProgram, 'uScale') - gl.programInfo.uniforms.contour = gl.ctx.getUniformLocation(gl.program as WebGLProgram, 'uScaleContour') - gl.programInfo.uniforms.width = gl.ctx.getUniformLocation(gl.program as WebGLProgram, 'uWidth') - - gl.programInfo.uniforms.maxLine = gl.ctx.getUniformLocation(gl.program as WebGLProgram, 'uMaxLine') - gl.programInfo.uniforms.color1 = gl.ctx.getUniformLocation(gl.program as WebGLProgram, 'uColor1') - gl.programInfo.uniforms.color2 = gl.ctx.getUniformLocation(gl.program as WebGLProgram, 'uColor2') - gl.programInfo.uniforms.background = gl.ctx.getUniformLocation(gl.program as WebGLProgram, 'uBackground') - } - - gl.pxratio = pxratio.value - gl.time += animationSpeed.value / 500 - - const c1 = hexToRgb(color1.value) - const c2 = hexToRgb(color2.value) - - gl.ctx.uniform1f(gl.programInfo.uniforms.time, gl.time) - gl.ctx.uniform1f(gl.programInfo.uniforms.scale, scale.value) - gl.ctx.uniform1f(gl.programInfo.uniforms.contour, contour.value) - gl.ctx.uniform1f(gl.programInfo.uniforms.width, width.value) - - gl.ctx.uniform1f(gl.programInfo.uniforms.maxLine, maxLine.value) - const bg = hexToRgb(background.value) - gl.ctx.uniform3f(gl.programInfo.uniforms.color1, c1[0] / 255, c1[1] / 255, c1[2] / 255) - gl.ctx.uniform3f(gl.programInfo.uniforms.color2, c2[0] / 255, c2[1] / 255, c2[2] / 255) - gl.ctx.uniform3f(gl.programInfo.uniforms.background, bg[0] / 255, bg[1] / 255, bg[2] / 255) - } - }, - { - radiusB: .5, intensityB: 1.0, - } - ) - render.running = true - }) - - onBeforeUnmount(() => { - render.running = false - }) - - return () => ( -
- -
- ) - } -}) diff --git a/src/components/themes/contour/vertex.glsl b/src/components/themes/contour/vertex.glsl deleted file mode 100644 index ab6ebcd..0000000 --- a/src/components/themes/contour/vertex.glsl +++ /dev/null @@ -1,6 +0,0 @@ -#version 300 es -in vec2 aVertexPosition; - -void main() { - gl_Position = vec4(aVertexPosition, 0.0, 1.0); -} diff --git a/src/components/themes/destruction/fragment.glsl b/src/components/themes/destruction/fragment.glsl deleted file mode 100644 index 6526e67..0000000 --- a/src/components/themes/destruction/fragment.glsl +++ /dev/null @@ -1,63 +0,0 @@ -#version 300 es -/* - * Original shader from: https://www.shadertoy.com/view/ldyyWm - */ -#ifdef GL_ES -precision highp float; -#endif - -uniform float uTime; -uniform float position; -uniform float perspective; -uniform vec2 uResolution; - -float burn; -float time; - -mat2 rot(float a) { - float s=sin(a), c=cos(a); - return mat2(s, c, -c, s); -} - -float map(vec3 p) { - float d = max(max(abs(p.x), abs(p.y)), abs(p.z)) - perspective; - burn = d; - - mat2 rm = rot(-time/3. + length(p)); - p.xy *= rm, p.zy *= rm; - - vec3 q = abs(p) - time; - q = abs(q - round(q)); - - rm = rot(time); - q.xy *= rm, q.xz *= rm; - - d = min(d, min(min(length(q.xy), length(q.yz)), length(q.xz)) + .01); - - burn = pow(d - burn, 2.); - - return d; -} - -void mainImage( out vec4 col, in vec2 fragCoord ) { - vec3 rd = normalize(vec3(2. * fragCoord - uResolution.xy, uResolution.y * position)), ro = vec2(0, -2).xxy; - - mat2 r1 = rot(time/4.), r2 = rot(time/2.); - rd.xz *= r1, ro.xz *= r1, rd.yz *= r2, ro.yz *= r2; - - float t = 0.0, i = 24.0 * (1.0 - exp(-0.2 * time - 0.1)); - for (int ii = 0; ii < 100; ++ii) { - if (i <= 0.0) break; - t += map(ro + rd * t) / 2.0; - i -= 1.0; - } - - col = vec4(1.1 - burn, exp(-t), exp(-t/2.), 1); - // col = vec4(exp(-t) * 4.1, exp(-t/2.) * 0.9, 0.4 - burn * 2.9, 1); -} - -out vec4 fragColor; -void main(void) { - time = uTime + 200.0; - mainImage(fragColor, gl_FragCoord.xy); -} \ No newline at end of file diff --git a/src/components/themes/destruction/index.tsx b/src/components/themes/destruction/index.tsx deleted file mode 100644 index a290d7c..0000000 --- a/src/components/themes/destruction/index.tsx +++ /dev/null @@ -1,56 +0,0 @@ -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: 'DestructionTheme', - setup() { - const themeStore = useThemeStore() - const canvasRef = ref(null) - - const perspective = computed(() => themeStore.getThemeInput('perspective')?.value as number || 0.2) - const position = computed(() => themeStore.getThemeInput('position')?.value as number || 0.2) - const animationSpeed = computed(() => themeStore.getThemeInput('animation-speed')?.value as number || 45) - const pxratio = computed(() => themeStore.getThemeInput('pxratio')?.value as number || 0.8) - - 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.position) { - gl.programInfo.uniforms.position = gl.ctx.getUniformLocation(gl.program as WebGLProgram, 'position') - gl.programInfo.uniforms.perspective = gl.ctx.getUniformLocation(gl.program as WebGLProgram, 'perspective') - } - gl.pxratio = pxratio.value - gl.time += animationSpeed.value / 500 - gl.ctx.uniform1f(gl.programInfo.uniforms.time, gl.time) - gl.ctx.uniform1f(gl.programInfo.uniforms.position, position.value) - gl.ctx.uniform1f(gl.programInfo.uniforms.perspective, perspective.value) - } - } - ) - render.running = true - }) - - onBeforeUnmount(() => { - render.running = false - }) - - return () => ( -
- -
- ) - } -}) diff --git a/src/components/themes/destruction/vertex.glsl b/src/components/themes/destruction/vertex.glsl deleted file mode 100644 index 2a559ce..0000000 --- a/src/components/themes/destruction/vertex.glsl +++ /dev/null @@ -1,6 +0,0 @@ -#version 300 es -in vec2 aVertexPosition; - -void main() { - gl_Position = vec4(aVertexPosition, 0.0, 1.0); -} \ No newline at end of file diff --git a/src/components/themes/flow/fragment.glsl b/src/components/themes/flow/fragment.glsl deleted file mode 100644 index 5c7238b..0000000 --- a/src/components/themes/flow/fragment.glsl +++ /dev/null @@ -1,78 +0,0 @@ -#version 300 es -precision highp float; - -uniform vec2 uResolution; -uniform float uTime; -uniform float size; - -float random (in vec2 point) { - return fract(100.0 * sin(point.x + fract(100.0 * sin(point.y)))); // http://www.matteo-basei.it/noise -} - -float noise (in vec2 st) { - vec2 i = floor(st); - vec2 f = fract(st); - - float a = random(i); - float b = random(i + vec2(1., 0.)); - float c = random(i + vec2(0., 1.)); - float d = random(i + vec2(1., 1.)); - - vec2 u = f * f * (3. - 2. * f); - - return mix(a, b, u.x) + (c - a)* u.y * (1.0 - u.x) + (d - b) * u.x * u.y; -} - -#define octaves 10 - -float fbm (in vec2 p) { - float value = 0.; - float freq = size; - float amp = .5; - - for (int i = 0; i < octaves; i++) { - value += amp * (noise((p - vec2(1.)) * freq)); - freq *= 1.9; - amp *= .6; - } - - return value; -} - -float pattern(in vec2 p) { - vec2 offset = vec2(-.5); - - vec2 aPos = vec2(sin(uTime * .05), sin(uTime * .1)) * 6.; - vec2 aScale = vec2(3.); - float a = fbm(p * aScale + aPos); - - vec2 bPos = vec2(sin(uTime * .1), sin(uTime * .1)) * 1.; - vec2 bScale = vec2(.5); - float b = fbm((p + a) * bScale + bPos); - - vec2 cPos = vec2(-.6, -.5) + vec2(sin(-uTime * .01), sin(uTime * .1)) * 2.; - vec2 cScale = vec2(2.); - float c = fbm((p + b) * cScale + cPos); - - return c; -} - -vec3 palette(in float t) { - vec3 a = vec3(.5, .5, .5); - vec3 b = vec3(.45, .25, .14); - vec3 c = vec3(1. ,1., 1.); - vec3 d = vec3(0., .1, .2); - - return a + b * cos(6.28318 * (c * t + d)); -} - -out vec4 fragColor; -void main() { - vec2 p = gl_FragCoord.xy / uResolution.xy; - p.x *= uResolution.x / uResolution.y; - - float value = pow(pattern(p), 2.); - vec3 color = palette(value); - - fragColor = vec4(color, 1.); -} \ No newline at end of file diff --git a/src/components/themes/flow/index.tsx b/src/components/themes/flow/index.tsx deleted file mode 100644 index b85e96c..0000000 --- a/src/components/themes/flow/index.tsx +++ /dev/null @@ -1,60 +0,0 @@ -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: 'FlowTheme', - setup() { - const themeStore = useThemeStore() - const canvasRef = ref(null) - - const hue = computed(() => themeStore.getThemeInput('hue')?.value as number || 0) - const brightness = computed(() => themeStore.getThemeInput('brightness')?.value as number || 0) - const invert = computed(() => themeStore.getThemeInput('invert')?.value as boolean || false) - const animationSpeed = computed(() => themeStore.getThemeInput('animation-speed')?.value as number || 45) - const size = computed(() => themeStore.getThemeInput('size')?.value as number || 0.2) - const pxratio = computed(() => themeStore.getThemeInput('pxratio')?.value as number || 0.8) - - const styleCanvas = computed(() => ({ - filter: `hue-rotate(${hue.value}deg) invert(${Number(invert.value)}) brightness(${brightness.value})` - })) - - 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.size) { - gl.programInfo.uniforms.size = gl.ctx.getUniformLocation(gl.program as WebGLProgram, 'size') - } - gl.pxratio = pxratio.value - gl.time += animationSpeed.value / 500 - gl.ctx.uniform1f(gl.programInfo.uniforms.time, gl.time) - gl.ctx.uniform1f(gl.programInfo.uniforms.size, size.value) - } - } - ) - render.running = true - }) - - onBeforeUnmount(() => { - render.running = false - }) - - return () => ( -
- -
- ) - } -}) diff --git a/src/components/themes/flow/vertex.glsl b/src/components/themes/flow/vertex.glsl deleted file mode 100644 index 2a559ce..0000000 --- a/src/components/themes/flow/vertex.glsl +++ /dev/null @@ -1,6 +0,0 @@ -#version 300 es -in vec2 aVertexPosition; - -void main() { - gl_Position = vec4(aVertexPosition, 0.0, 1.0); -} \ No newline at end of file diff --git a/src/components/themes/planet/fragment.glsl b/src/components/themes/planet/fragment.glsl deleted file mode 100644 index a73085c..0000000 --- a/src/components/themes/planet/fragment.glsl +++ /dev/null @@ -1,186 +0,0 @@ -#version 300 es -/* - * Original shader from: https://www.shadertoy.com/view/ttKBDd - */ - -#ifdef GL_ES -precision highp float; -#endif - -uniform float camR; -uniform float uTime; -uniform vec2 uResolution; -uniform vec2 iMouse; - -#define texture(s, uv) vec4(0.0) - -#define R uResolution.xy - -#define m vec2(R.x/R.y*(iMouse.x/R.x-.5),iMouse.y/R.y-.5) -#define ss(a, b, t) smoothstep(a, b, t) -#define rot(a) mat2(cos(a), -sin(a), sin(a), cos(a)) -const float pi = 3.14159; - -float hsh(vec2 p) { - vec3 p3 = fract(vec3(p.xyx) * .1031); - p3 += dot(p3, p3.yzx + 33.33); - return fract((p3.x + p3.y) * p3.z); -} - -float perlin(vec2 p) { - vec2 i = floor(p); - vec2 f = fract(p); - - float a = hsh(i); - float b = hsh(i+vec2(1., .0)); - float c = hsh(i+vec2(0. ,1 )); - float d = hsh(i+vec2(1., 1. )); - - vec2 u = smoothstep(0., 1., f); - - return mix(a, b, u.x) + (c - a) * u.y * (1.0 - u.x) + (d - b) * u.x * u.y; -} - -float octnse(vec2 p, int oct, float t) { - float a = 1.; - float n = 0.; - - for(int i = 0; i < 10; i++){ - if (i >= oct) break; - p.x += t; - n += perlin(p) * a *.5; - p*=2.; - a *= .5; - } - - return n; -} - - -// 3D simplex noise stuff from: https://www.shadertoy.com/view/XsX3zB -const float F3 = .3333333; -const float G3 = .1666667; - -vec3 random3(vec3 c) { - float j = 4096.*sin(dot(c,vec3(17., 59.4, 15.))); - vec3 r; - r.z = fract(512.*j); - j *= .125; - r.x = fract(512.*j); - j *= .125; - r.y = fract(512.*j); - return r-.5; -} - -float simplex3d(vec3 p) { - vec3 s = floor(p + dot(p, vec3(F3))); - vec3 x = p - s + dot(s, vec3(G3)); - vec3 e = step(vec3(0.), x - x.yzx); - vec3 i1 = e*(1. - e.zxy); - vec3 i2 = 1. - e.zxy*(1. - e); - vec3 x1 = x - i1 + G3; - vec3 x2 = x - i2 + 2.*G3; - vec3 x3 = x - 1. + 3.*G3; - vec4 w, d; - w.x = dot(x, x); - w.y = dot(x1, x1); - w.z = dot(x2, x2); - w.w = dot(x3, x3); - w = max(.6 - w, 0.); - d.x = dot(random3(s), x); - d.y = dot(random3(s + i1), x1); - d.z = dot(random3(s + i2), x2); - d.w = dot(random3(s + 1.), x3); - w *= w; - w *= w; - d *= w; - - float nse = dot(d, vec4(52.)); - - return 1.-exp(-(nse+1.)*.5); -} - -vec4 sphere(vec3 ro, vec3 rd, vec3 cn, float r) { - float b = 2.*dot(rd, ro - cn); - float c = dot(ro - cn, ro - cn) - (r*r); - float d = (b*b) - (4.*c); - - if (d < 0.) { - return vec4(0); - } else { - float t = .5*(-b - sqrt(d)); - return vec4(ro+rd*t, t); - } -} - -const float rad = 2.7; - -void mainImage( out vec4 f, in vec2 u ) { - vec2 uv = vec2(u.xy - 0.5*R.xy)/R.y; - float ux = uv.x; - uv *= rot(-uTime*.12 + 2.2); - - vec3 ro = vec3(0., 0., 0.); - vec3 rd = normalize(vec3(uv, 1.)); - float camP = camR; - - float ang = uTime*.12 + 7.; - - if (iMouse.y > 0.){ - camP -= m.y * 6.; - ang += m.x; - } - - ro.x += camP * cos(ang); - ro.z += camP * sin(ang); - - rd.xz *= rot(ang + pi/2. + .04); - - vec3 ld = normalize(vec3(0.4, 0.3, -0.5)); - - float ts = .5; - - vec3 pp = vec3(0); - vec3 n = vec3(0); - - vec3 cntr = vec3(0., 0., 0.); - vec4 p = sphere(ro, rd, cntr, rad); - - vec3 col = vec3(0); - - pp = p.xyz; - n = pp - cntr; - n = normalize(n); - - vec2 cuv = abs(vec2(atan(n.z, n.x), acos(p.y / rad))); - cuv *= rot(-uTime*.05 * ts); - - float n1 = 2.*octnse(cuv, 10, -uTime*.08 * ts) - 1.; - float n2 = 2.*octnse((cuv+3.), 10, -uTime*.03 * ts) - 1.; - - vec2 os = vec2(n1, n2); - - float val = octnse((cuv + vec2(n1, n2)*3.6), 8, -uTime*.1 * ts); - - col += .35+.35*cos(vec3(1.4, .7, 0.9)* n1 * 10. + uTime * .35); - col += .48+.37*sin(vec3(2.2, .1, 0.3)* n2 * 20. + vec3(.7, 1.2, .7)); - col += .48+.23*cos(vec3(1.4, .7, 0.9)* val * 30. + vec3(.2, 0.8, 4.7)+ uTime*.25); - col*=.38; - - vec3 ref = reflect(n, rd); - float val2 = octnse((ref.xy + os*10. + val*5.), 8, -uTime*.1 * ts); - - col *= val2*3. * vec3(.9, .8, .8); - col *= max(dot(n, -rd), 0.0)*vec3(.9, .8, .7); - - col = col*col*1.7; - - col = 1.-exp(-col); - - f = vec4(col, 1.0); -} - -out vec4 fragColor; -void main(void) { - mainImage(fragColor, gl_FragCoord.xy); -} \ No newline at end of file diff --git a/src/components/themes/planet/index.tsx b/src/components/themes/planet/index.tsx deleted file mode 100644 index b81cf7b..0000000 --- a/src/components/themes/planet/index.tsx +++ /dev/null @@ -1,53 +0,0 @@ -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: 'PlanetTheme', - setup() { - const themeStore = useThemeStore() - const canvasRef = ref(null) - - const animationSpeed = computed(() => themeStore.getThemeInput('animation-speed')?.value as number || 45) - const position = computed(() => themeStore.getThemeInput('position')?.value as number || 2.14) - const pxratio = computed(() => themeStore.getThemeInput('pxratio')?.value as number || 0.8) - - 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.position) { - gl.programInfo.uniforms.position = gl.ctx.getUniformLocation(gl.program as WebGLProgram, 'camR') - } - gl.pxratio = pxratio.value - gl.time += animationSpeed.value / 500 - gl.ctx.uniform1f(gl.programInfo.uniforms.time, gl.time + (gl.time * animationSpeed.value / 10)) - gl.ctx.uniform1f(gl.programInfo.uniforms.position, position.value) - } - } - ) - render.running = true - }) - - onBeforeUnmount(() => { - render.running = false - }) - - return () => ( -
- -
- ) - } -}) diff --git a/src/components/themes/planet/vertex.glsl b/src/components/themes/planet/vertex.glsl deleted file mode 100644 index 2a559ce..0000000 --- a/src/components/themes/planet/vertex.glsl +++ /dev/null @@ -1,6 +0,0 @@ -#version 300 es -in vec2 aVertexPosition; - -void main() { - gl_Position = vec4(aVertexPosition, 0.0, 1.0); -} \ No newline at end of file diff --git a/src/components/themes/plasma/fragment.glsl b/src/components/themes/plasma/fragment.glsl deleted file mode 100644 index 89d7d4f..0000000 --- a/src/components/themes/plasma/fragment.glsl +++ /dev/null @@ -1,50 +0,0 @@ -#version 300 es -/** - * Original shader: https://www.shadertoy.com/view/MsjSW3 - */ -precision highp float; - -uniform float uTime; -uniform vec2 uResolution; - -#define t uTime - -mat2 m(float a){float c=cos(a), s=sin(a);return mat2(c,-s,s,c);} - -float map(vec3 p){ - p.xz*= m(t * 0.4); - p.xy*= m(t * 0.3); - - float complexity = 2.; - vec3 q = p * complexity + t; - - float zoom = 0.1; - float size = .01; - return length(p + vec3(sin(t * .7))) * log(length(p) + size) + sin(q.x + sin(q.z + sin(q.y))) * 0.5 - zoom; -} - -out vec4 fragColor; -void main() { - vec2 p = gl_FragCoord.xy / uResolution.y - vec2(0.9,.5); - vec3 cl = vec3(0.); - float saturation = 4.; - float scale = -1.0; - - for (int i=0; i <= 5; i++) { - vec3 p = vec3(0, 0, 5.) + normalize(vec3(p, scale)) * saturation; - float rz = map(p); - float glow = 0.5; - float varietyOfColors = 0.1; - float f = clamp((rz - map(p + varietyOfColors)) * glow, - .1, 1.); - vec3 l = vec3(0.102, 0.1059, 0.4) + vec3(5., 2.5, 3.) * f; - - float contrast = 0.2; - float hue = 0.; - cl = cl * l + smoothstep(2.5, .0, rz) * .7 * l; - - float smoothness = .9; - saturation += min(rz, smoothness); - } - - fragColor = vec4(cl, 1.); -} \ No newline at end of file diff --git a/src/components/themes/plasma/index.tsx b/src/components/themes/plasma/index.tsx deleted file mode 100644 index b89cbc0..0000000 --- a/src/components/themes/plasma/index.tsx +++ /dev/null @@ -1,53 +0,0 @@ -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: 'PlasmaTheme', - setup() { - const themeStore = useThemeStore() - const canvasRef = ref(null) - - const hue = computed(() => themeStore.getThemeInput('hue')?.value as number || 0) - const animationSpeed = computed(() => themeStore.getThemeInput('animation-speed')?.value as number || 45) - const pxratio = computed(() => themeStore.getThemeInput('pxratio')?.value as number || 0.8) - - const styleCanvas = computed(() => ({ - filter: `hue-rotate(${hue.value}deg)` - })) - - onMounted(() => { - render = new GL( - canvasRef.value!, - vertexShader, - fragmentShader, - window.innerWidth, - window.innerHeight, - { - renderOptions: { externalTimeUse: true }, - renderHook() { - const gl = this as unknown as GL - gl.pxratio = pxratio.value - gl.time += animationSpeed.value / 500 - gl.ctx.uniform1f(gl.programInfo.uniforms.time, gl.time) - } - } - ) - render.running = true - }) - - onBeforeUnmount(() => { - render.running = false - }) - - return () => ( -
- -
- ) - } -}) diff --git a/src/components/themes/plasma/vertex.glsl b/src/components/themes/plasma/vertex.glsl deleted file mode 100644 index 2a559ce..0000000 --- a/src/components/themes/plasma/vertex.glsl +++ /dev/null @@ -1,6 +0,0 @@ -#version 300 es -in vec2 aVertexPosition; - -void main() { - gl_Position = vec4(aVertexPosition, 0.0, 1.0); -} \ No newline at end of file diff --git a/src/components/themes/random/fragment.glsl b/src/components/themes/random/fragment.glsl deleted file mode 100644 index 28020d2..0000000 --- a/src/components/themes/random/fragment.glsl +++ /dev/null @@ -1,30 +0,0 @@ -#version 300 es -#ifdef GL_ES -precision highp float; -#endif - -uniform float uTime; -uniform vec2 uResolution; -uniform float symmetry; -uniform float thickness; - -float f(vec3 x) { - x.z -= uTime; - float a = x.z * symmetry; - x.xy *= mat2(cos(a), sin(a), -sin(a), cos(a)); - return thickness - length(cos(x.xy) + sin(x.yz)); -} - -vec3 lambda_0(vec3 x) { - return x + f(x) * (0.5 - (vec3(gl_FragCoord.xy, 1.0) / uResolution.x)); -} - -out vec4 fragColor; -void main() { - vec3 i_0 = vec3(0.0,0.0,0.0); - - for (float i_1 = 0.0; i_1 < 32.; i_1++) i_0 = lambda_0(i_0); - - vec3 p = i_0; - fragColor = vec4(((vec3(2.0,5.0,9.0) + sin(p)) / length(p)),1.0); -} \ No newline at end of file diff --git a/src/components/themes/random/index.tsx b/src/components/themes/random/index.tsx deleted file mode 100644 index 048787e..0000000 --- a/src/components/themes/random/index.tsx +++ /dev/null @@ -1,63 +0,0 @@ -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: 'RandomTheme', - setup() { - const themeStore = useThemeStore() - const canvasRef = ref(null) - - const hue = computed(() => themeStore.getThemeInput('hue')?.value as number || 0) - const brightness = computed(() => themeStore.getThemeInput('brightness')?.value as number || 0) - const invert = computed(() => themeStore.getThemeInput('invert')?.value as boolean || false) - const animationSpeed = computed(() => themeStore.getThemeInput('animation-speed')?.value as number || 45) - const symmetry = computed(() => themeStore.getThemeInput('symmetry')?.value as number || 64) - const thickness = computed(() => themeStore.getThemeInput('thickness')?.value as number || 64) - const pxratio = computed(() => themeStore.getThemeInput('pxratio')?.value as number || 0.8) - - const styleCanvas = computed(() => ({ - filter: `hue-rotate(${hue.value}deg) invert(${Number(invert.value)}) brightness(${brightness.value})` - })) - - 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.symmetry) { - gl.programInfo.uniforms.symmetry = gl.ctx.getUniformLocation(gl.program as WebGLProgram, 'symmetry') - gl.programInfo.uniforms.thickness = gl.ctx.getUniformLocation(gl.program as WebGLProgram, 'thickness') - } - gl.pxratio = pxratio.value - gl.time += animationSpeed.value / 500 - gl.ctx.uniform1f(gl.programInfo.uniforms.symmetry, symmetry.value) - gl.ctx.uniform1f(gl.programInfo.uniforms.thickness, thickness.value) - gl.ctx.uniform1f(gl.programInfo.uniforms.time, gl.time + (gl.time * animationSpeed.value / 10)) - } - } - ) - render.running = true - }) - - onBeforeUnmount(() => { - render.running = false - }) - - return () => ( -
- -
- ) - } -}) diff --git a/src/components/themes/random/vertex.glsl b/src/components/themes/random/vertex.glsl deleted file mode 100644 index 2a559ce..0000000 --- a/src/components/themes/random/vertex.glsl +++ /dev/null @@ -1,6 +0,0 @@ -#version 300 es -in vec2 aVertexPosition; - -void main() { - gl_Position = vec4(aVertexPosition, 0.0, 1.0); -} \ No newline at end of file diff --git a/src/components/themes/rings/fragment.glsl b/src/components/themes/rings/fragment.glsl deleted file mode 100644 index 80bc758..0000000 --- a/src/components/themes/rings/fragment.glsl +++ /dev/null @@ -1,41 +0,0 @@ -#version 300 es -precision highp float; - -uniform float uTime; -uniform float hue; -uniform float zoom; // 32.415 -uniform vec2 uResolution; -float linearity = 2.; // 2. - -out vec4 fragColor; -void main() { - float mx = max( uResolution.x, uResolution.y ); - vec2 uv = (gl_FragCoord.xy - uResolution.xy * 0.5) / mx; - uv.x += sin(uTime + uv.y * linearity) * .2; - - uv.y -= cos(uTime) * 0.1; - - uv.x = dot(uv,uv)*2.0; - - float angle = .4; - uv *= mat2(cos(angle), sin(angle), sin(angle), cos(angle)); - - float fineness = mx * 0.4; // 0 + 0.4 - // float fineness = mx * (abs(hue) + 0.2); // 0 + 0.4 - float sy = uv.y * fineness; - float c = fract(-sin(floor(sy) / fineness * 14.) * 437.); - - float f = fract(sy); - c *= min(f, 1. - f) * 3.; - - // float zoom = 32.415; - float intensity = 2.5; - // highlights - c += cos(uv.y * zoom - uTime) * intensity; - - // background - float r = -uv.y + (.5 + hue); - float b = uv.y + (.5 - hue); - - fragColor = vec4(mix(vec3(r, r*.3, b), vec3(c), .3), 1.0); -} \ No newline at end of file diff --git a/src/components/themes/rings/index.tsx b/src/components/themes/rings/index.tsx deleted file mode 100644 index 2a61924..0000000 --- a/src/components/themes/rings/index.tsx +++ /dev/null @@ -1,56 +0,0 @@ -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: 'RingsTheme', - setup() { - const themeStore = useThemeStore() - const canvasRef = ref(null) - - const hue = computed(() => themeStore.getThemeInput('hue')?.value as number || 0) - const animationSpeed = computed(() => themeStore.getThemeInput('animation-speed')?.value as number || 45) - const zoom = computed(() => themeStore.getThemeInput('zoom')?.value as number || 32) - const pxratio = computed(() => themeStore.getThemeInput('pxratio')?.value as number || 0.8) - - 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.hue) { - gl.programInfo.uniforms.hue = gl.ctx.getUniformLocation(gl.program as WebGLProgram, 'hue') - gl.programInfo.uniforms.zoom = gl.ctx.getUniformLocation(gl.program as WebGLProgram, 'zoom') - } - gl.time += animationSpeed.value / 500 - gl.pxratio = pxratio.value - gl.ctx.uniform1f(gl.programInfo.uniforms.time, gl.time) - gl.ctx.uniform1f(gl.programInfo.uniforms.hue, hue.value) - gl.ctx.uniform1f(gl.programInfo.uniforms.zoom, zoom.value) - } - } - ) - render.running = true - }) - - onBeforeUnmount(() => { - render.running = false - }) - - return () => ( -
- -
- ) - } -}) diff --git a/src/components/themes/rings/vertex.glsl b/src/components/themes/rings/vertex.glsl deleted file mode 100644 index 2a559ce..0000000 --- a/src/components/themes/rings/vertex.glsl +++ /dev/null @@ -1,6 +0,0 @@ -#version 300 es -in vec2 aVertexPosition; - -void main() { - gl_Position = vec4(aVertexPosition, 0.0, 1.0); -} \ No newline at end of file diff --git a/src/components/themes/rings3d/fragment.glsl b/src/components/themes/rings3d/fragment.glsl deleted file mode 100644 index 5097699..0000000 --- a/src/components/themes/rings3d/fragment.glsl +++ /dev/null @@ -1,152 +0,0 @@ -#version 300 es -// Inspiration: https://www.shadertoy.com/view/WdB3Dw -#ifdef GL_ES -precision highp float; -#endif - -uniform float uTime; -uniform vec2 uResolution; - -// Torus tube cross-section radius — thicker values widen the ring walls -uniform float uTubeSize; - -// Clipping sphere radius — controls how large the bubble appears -uniform float uBubbleSize; - -// Ray march step fraction — lower = slower rays, denser glow/fog accumulation -uniform float uFogDensity; - -// Spectrum cycling frequency — higher = more colour bands visible per ray -uniform float uSpectrumSpeed; - -// Surface glow colour (normalised; internally scaled ×2.1 to allow overbright) -uniform vec3 uGlowColor; - -// Ambient scatter/fog colour accumulated on every ray march step -uniform vec3 uFogColor; - -#define PI 3.14159265359 - -void pR(inout vec2 p, float a) { - p = cos(a)*p + sin(a)*vec2(p.y, -p.x); -} - -float smax(float a, float b, float r) { - vec2 u = max(vec2(r + a, r + b), vec2(0.0)); - return min(-r, max(a, b)) + length(u); -} - -// ─── Spectrum palette ──────────────────────────────────────────────────────── -// Cosine colour palette — IQ https://www.shadertoy.com/view/ll2GD3 - -vec3 pal(in float t, in vec3 a, in vec3 b, in vec3 c, in vec3 d) { - return a + b*cos(6.28318*(c*t+d)); -} - -vec3 spectrum(float n) { - return pal(n, vec3(0.5,0.5,0.5), vec3(0.5,0.5,0.5), - vec3(1.0,1.0,1.0), vec3(0.0,0.33,0.67)); -} - -// ─── SDF via inverse stereographic projection ──────────────────────────────── - -vec4 inverseStereographic(vec3 p, out float k) { - k = 2.0 / (1.0 + dot(p, p)); - return vec4(k*p, k - 1.0); -} - -float fTorus(vec4 p4) { - float d1 = length(p4.xy) / length(p4.zw) - 1.0; - float d2 = length(p4.zw) / length(p4.xy) - 1.0; - float d = d1 < 0.0 ? -d1 : d2; - d /= PI; - return d; -} - -float fixDistance(float d, float k) { - float sn = sign(d); - d = abs(d); - d = d / k * 1.82; - d += 1.0; - d = pow(d, 0.5); - d -= 1.0; - d *= 5.0 / 3.0; - d *= sn; - return d; -} - -float gTime; - -float map(vec3 p) { - float k; - vec4 p4 = inverseStereographic(p, k); - pR(p4.zy, gTime * -PI / 2.0); - pR(p4.xw, gTime * -PI / 2.0); - float d = fTorus(p4); - d = abs(d); - d -= uTubeSize; - d = fixDistance(d, k); - d = smax(d, length(p) - uBubbleSize, 0.2); - return d; -} - -// ─── Rendering ─────────────────────────────────────────────────────────────── - -mat3 calcLookAtMatrix(vec3 ro, vec3 ta, vec3 up) { - vec3 ww = normalize(ta - ro); - vec3 uu = normalize(cross(ww, up)); - vec3 vv = normalize(cross(uu, ww)); - return mat3(uu, vv, ww); -} - -out vec4 fragColor; -void main() { - gTime = mod(uTime / 2.0, 1.0); - - vec3 camPos = vec3(1.8, 5.5, -5.5) * 1.75; - vec3 camTar = vec3(0.0, 0.0, 0.0); - vec3 camUp = vec3(-1.0, 0.0, -1.5); - mat3 camMat = calcLookAtMatrix(camPos, camTar, camUp); - - float focalLength = 5.0; - vec2 p = (-uResolution.xy + 2.0 * gl_FragCoord.xy) / uResolution.y; - - vec3 rayDir = normalize(camMat * vec3(p, focalLength)); - vec3 rayPos = camPos; - float rayLen = 0.0; - float dist = 0.0; - vec3 color = vec3(0.0); - - const int ITER = 82; - const float MIN_STEP = 0.001; - const float MAX_DIST = 20.0; - - for (int i = 0; i < ITER; i++) { - rayLen += max(MIN_STEP, abs(dist) * uFogDensity); - rayPos = camPos + rayDir * rayLen; - dist = map(rayPos); - - // Surface glow: bright flare near the SDF surface, tinted by uGlowColor - vec3 c = vec3(max(0.0, 0.01 - abs(dist)) * 0.5); - c *= uGlowColor * 2.1; // ×2.1 restores the original overbright range - - // Ambient fog: coloured scatter accumulated on every step - c += uFogColor * uFogDensity / 160.0; - - c *= smoothstep(20.0, 7.0, length(rayPos)); - - float rl = smoothstep(MAX_DIST, 0.1, rayLen); - c *= rl; - c *= spectrum(rl * uSpectrumSpeed - 0.6); - - color += c; - - if (rayLen > MAX_DIST) break; - } - - color = pow(color, vec3(1.0 / 1.8)) * 2.0; - color = pow(color, vec3(2.0)) * 3.0; - color = pow(color, vec3(1.0 / 2.2)); - - fragColor = vec4(color, 1.0); -} diff --git a/src/components/themes/rings3d/index.tsx b/src/components/themes/rings3d/index.tsx deleted file mode 100644 index 06db0f1..0000000 --- a/src/components/themes/rings3d/index.tsx +++ /dev/null @@ -1,75 +0,0 @@ -import { defineComponent, ref, computed, onMounted, onBeforeUnmount } from 'vue' -import GL from '@/utils/gl' -import { useThemeStore } from '@/store/theme' -import { hexToRgb } from '@/utils/color' -import fragmentShader from './fragment.glsl' -import vertexShader from './vertex.glsl' - -let render: GL - -export default defineComponent({ - name: 'Rings3dTheme', - 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 tubeSize = computed(() => themeStore.getThemeInput('tube-size')?.value as number || 0.2) - const bubbleSize = computed(() => themeStore.getThemeInput('bubble-size')?.value as number || 1.85) - const fogDensity = computed(() => themeStore.getThemeInput('fog-density')?.value as number || 0.8) - const spectrumSpeed = computed(() => themeStore.getThemeInput('spectrum-speed')?.value as number || 6.0) - const glowColor = computed(() => themeStore.getThemeInput('color-glow')?.value as string || '#AAFFCE') - const fogColor = computed(() => themeStore.getThemeInput('color-fog')?.value as string || '#9940B3') - - 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.tubeSize) { - gl.programInfo.uniforms.tubeSize = gl.ctx.getUniformLocation(gl.program as WebGLProgram, 'uTubeSize') - gl.programInfo.uniforms.bubbleSize = gl.ctx.getUniformLocation(gl.program as WebGLProgram, 'uBubbleSize') - gl.programInfo.uniforms.fogDensity = gl.ctx.getUniformLocation(gl.program as WebGLProgram, 'uFogDensity') - gl.programInfo.uniforms.spectrumSpeed = gl.ctx.getUniformLocation(gl.program as WebGLProgram, 'uSpectrumSpeed') - gl.programInfo.uniforms.glowColor = gl.ctx.getUniformLocation(gl.program as WebGLProgram, 'uGlowColor') - gl.programInfo.uniforms.fogColor = gl.ctx.getUniformLocation(gl.program as WebGLProgram, 'uFogColor') - } - - gl.pxratio = pxratio.value - gl.time += animationSpeed.value / 500 - - const gc = hexToRgb(glowColor.value) - const fc = hexToRgb(fogColor.value) - - gl.ctx.uniform1f(gl.programInfo.uniforms.time, gl.time) - gl.ctx.uniform1f(gl.programInfo.uniforms.tubeSize, tubeSize.value) - gl.ctx.uniform1f(gl.programInfo.uniforms.bubbleSize, bubbleSize.value) - gl.ctx.uniform1f(gl.programInfo.uniforms.fogDensity, fogDensity.value) - gl.ctx.uniform1f(gl.programInfo.uniforms.spectrumSpeed, spectrumSpeed.value) - gl.ctx.uniform3f(gl.programInfo.uniforms.glowColor, gc[0] / 255, gc[1] / 255, gc[2] / 255) - gl.ctx.uniform3f(gl.programInfo.uniforms.fogColor, fc[0] / 255, fc[1] / 255, fc[2] / 255) - } - } - ) - render.running = true - }) - - onBeforeUnmount(() => { - render.running = false - }) - - return () => ( -
- -
- ) - } -}) diff --git a/src/components/themes/rings3d/vertex.glsl b/src/components/themes/rings3d/vertex.glsl deleted file mode 100644 index ab6ebcd..0000000 --- a/src/components/themes/rings3d/vertex.glsl +++ /dev/null @@ -1,6 +0,0 @@ -#version 300 es -in vec2 aVertexPosition; - -void main() { - gl_Position = vec4(aVertexPosition, 0.0, 1.0); -} diff --git a/src/components/themes/sphere/fragment.glsl b/src/components/themes/sphere/fragment.glsl deleted file mode 100644 index ee6086d..0000000 --- a/src/components/themes/sphere/fragment.glsl +++ /dev/null @@ -1,63 +0,0 @@ -#version 300 es -/* - * Original shader from: https://www.shadertoy.com/view/lslcWj - */ - -#ifdef GL_ES -precision highp float; -#endif - -uniform float uTime; -uniform vec2 uResolution; -uniform float size; - -#define R(p, a) p = p * cos(a) + vec2(-p.y, p.x) * sin(a) - -float Sin01(float t) { - return 0.5 + 0.5 * sin(6.28319 * t); -} - -float SineEggCarton(vec3 p) { - return 1.0 - abs(sin(p.x) + sin(p.y) + sin(p.z)) / 3.0; -} - -float Map(vec3 p, float scale) { - float dSphere = length(p) - 1.0; - return max(dSphere, (0.95 - SineEggCarton(scale * p)) / scale); -} - -vec3 GetColor(vec3 p) { - float amount = clamp((1.5 - length(p)) / 2.0, 0.0, 1.0); - vec3 col = 0.5 + 0.5 * cos(6.28319 * (vec3(0.2, 0.0, 0.0) + amount * vec3(1.0, 1.0, 0.5))); - return col * amount; -} - -void mainImage(out vec4 col, in vec2 fragCoord) { - vec3 rd = normalize(vec3(2.0 * fragCoord.xy - uResolution.xy, -uResolution.y)); - vec3 ro = vec3(0.0, 0.0, size); - - R(rd.xz, 0.5 * uTime); - R(ro.xz, 0.5 * uTime); - R(rd.yz, 0.5 * uTime); - R(ro.yz, 0.5 * uTime); - - float t = 0.0; - col.rgb = vec3(0.0353, 0.0275, 0.1255); - float scale = mix(3.5, 9.0, Sin01(0.068 * uTime)); - - for (int i = 0; i < 64; i++) { - vec3 p = ro + t * rd; - float d = Map(p, scale); - if (t > 5.0 || d < 0.001) { - break; - } - t += .9 * d; - col.rgb += 0.05 * GetColor(p); - } -} - -out vec4 fragColor; -void main(void) { - mainImage(fragColor, gl_FragCoord.xy); - fragColor.a = 1.0; -} \ No newline at end of file diff --git a/src/components/themes/sphere/index.tsx b/src/components/themes/sphere/index.tsx deleted file mode 100644 index b326a21..0000000 --- a/src/components/themes/sphere/index.tsx +++ /dev/null @@ -1,60 +0,0 @@ -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: 'SphereTheme', - setup() { - const themeStore = useThemeStore() - const canvasRef = ref(null) - - const hue = computed(() => themeStore.getThemeInput('hue')?.value as number || 0) - const brightness = computed(() => themeStore.getThemeInput('brightness')?.value as number || 0) - const invert = computed(() => themeStore.getThemeInput('invert')?.value as boolean || false) - const animationSpeed = computed(() => themeStore.getThemeInput('animation-speed')?.value as number || 45) - const size = computed(() => themeStore.getThemeInput('size')?.value as number || 0.2) - const pxratio = computed(() => themeStore.getThemeInput('pxratio')?.value as number || 0.8) - - const styleCanvas = computed(() => ({ - filter: `hue-rotate(${hue.value}deg) invert(${Number(invert.value)}) brightness(${brightness.value})` - })) - - 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.size) { - gl.programInfo.uniforms.size = gl.ctx.getUniformLocation(gl.program as WebGLProgram, 'size') - } - gl.pxratio = pxratio.value - gl.time += animationSpeed.value / 500 - gl.ctx.uniform1f(gl.programInfo.uniforms.time, gl.time) - gl.ctx.uniform1f(gl.programInfo.uniforms.size, size.value) - } - } - ) - render.running = true - }) - - onBeforeUnmount(() => { - render.running = false - }) - - return () => ( -
- -
- ) - } -}) diff --git a/src/components/themes/sphere/vertex.glsl b/src/components/themes/sphere/vertex.glsl deleted file mode 100644 index 2a559ce..0000000 --- a/src/components/themes/sphere/vertex.glsl +++ /dev/null @@ -1,6 +0,0 @@ -#version 300 es -in vec2 aVertexPosition; - -void main() { - gl_Position = vec4(aVertexPosition, 0.0, 1.0); -} \ No newline at end of file diff --git a/src/components/themes/tenderness/fragment.glsl b/src/components/themes/tenderness/fragment.glsl deleted file mode 100644 index 333a0c2..0000000 --- a/src/components/themes/tenderness/fragment.glsl +++ /dev/null @@ -1,28 +0,0 @@ -#version 300 es -// Modified so it doesn't really move. Very childish and easy fix. -#ifdef GL_ES -precision highp float; -#endif - -uniform vec2 uResolution; -uniform float uTime; - -const int complexity = 30; // More points of color. -const float fluidSpeed = 60.0; // Drives speed, higher number will make it slower. -const float color_intensity = 0.5; -const float position = 1.0; - -out vec4 fragColor; -void main() { - vec2 p= (position * gl_FragCoord.xy - uResolution) / max(uResolution.x, uResolution.y); - - for(int i = 1; i < complexity;i++) { - vec2 newp= p + uTime * 0.005; - newp.x+=0.6/float(i)*sin(float(i)*p.y+uTime/fluidSpeed+20.3*float(i)) + 0.5; // + mouse.y/mouse_factor+mouse_offset; - newp.y+=0.6/float(i)*sin(float(i)*p.x+uTime/fluidSpeed+0.3*float(i+10)) - 0.5; // - mouse.x/mouse_factor+mouse_offset; - p=newp; - } - - vec3 col=vec3(color_intensity*sin(5.0*p.x)+color_intensity,color_intensity*sin(3.0*p.y)+color_intensity,color_intensity*sin(p.x+p.y)+color_intensity); - fragColor=vec4(col, 1.0); -} \ No newline at end of file diff --git a/src/components/themes/tenderness/index.tsx b/src/components/themes/tenderness/index.tsx deleted file mode 100644 index d07329f..0000000 --- a/src/components/themes/tenderness/index.tsx +++ /dev/null @@ -1,48 +0,0 @@ -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: 'TendernessTheme', - setup() { - const themeStore = useThemeStore() - const canvasRef = ref(null) - - const animationSpeed = computed(() => themeStore.getThemeInput('animation-speed')?.value as number || 45) - const pxratio = computed(() => themeStore.getThemeInput('pxratio')?.value as number || 0.8) - - onMounted(() => { - render = new GL( - canvasRef.value!, - vertexShader, - fragmentShader, - window.innerWidth, - window.innerHeight, - { - renderOptions: { externalTimeUse: true }, - renderHook() { - const gl = this as unknown as GL - gl.time += animationSpeed.value / 500 - gl.pxratio = pxratio.value - gl.ctx.uniform1f(gl.programInfo.uniforms.time, gl.time) - } - } - ) - render.running = true - }) - - onBeforeUnmount(() => { - render.running = false - }) - - return () => ( -
- -
- ) - } -}) diff --git a/src/components/themes/tenderness/vertex.glsl b/src/components/themes/tenderness/vertex.glsl deleted file mode 100644 index 2a559ce..0000000 --- a/src/components/themes/tenderness/vertex.glsl +++ /dev/null @@ -1,6 +0,0 @@ -#version 300 es -in vec2 aVertexPosition; - -void main() { - gl_Position = vec4(aVertexPosition, 0.0, 1.0); -} \ No newline at end of file diff --git a/src/components/themes/tunnel/fragment.glsl b/src/components/themes/tunnel/fragment.glsl deleted file mode 100644 index de57bbe..0000000 --- a/src/components/themes/tunnel/fragment.glsl +++ /dev/null @@ -1,45 +0,0 @@ -#version 300 es -// 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 - -out vec4 fragColor; -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); - } - fragColor = vec4(c / l, 1.0); -} diff --git a/src/components/themes/tunnel/index.tsx b/src/components/themes/tunnel/index.tsx deleted file mode 100644 index a6c01c4..0000000 --- a/src/components/themes/tunnel/index.tsx +++ /dev/null @@ -1,64 +0,0 @@ -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 deleted file mode 100644 index ab6ebcd..0000000 --- a/src/components/themes/tunnel/vertex.glsl +++ /dev/null @@ -1,6 +0,0 @@ -#version 300 es -in vec2 aVertexPosition; - -void main() { - gl_Position = vec4(aVertexPosition, 0.0, 1.0); -} diff --git a/src/components/themes/zappy/fragment.glsl b/src/components/themes/zappy/fragment.glsl deleted file mode 100644 index 9e96f88..0000000 --- a/src/components/themes/zappy/fragment.glsl +++ /dev/null @@ -1,71 +0,0 @@ -#version 300 es -// Inspiration: https://www.shadertoy.com/view/cfjGzV — Zippy Zaps -// Credits: -13 thanks to Nguyen2007 ⚡ -#ifdef GL_ES -precision highp float; -#endif - -uniform float uTime; -uniform vec2 uResolution; - -// Overall zoom / scale of the electric pattern -uniform float uZoom; - -// Shifts the base colour phase vector, cycling through hue combinations -uniform float uColorShift; - - -out vec4 fragColor; -void main() { - vec2 res = uResolution; - vec2 u = gl_FragCoord.xy; - - // Centre and normalise to screen height; uZoom replaces the hardcoded 0.2 - u = uZoom * (u + u - res) / res.y; - - // Base colour phase vector — uColorShift scrolls through hue space - // z.wxzw swizzle = vec4(z.w, z.x, z.z, z.w) = vec4(0,1,3,0) used in mat2 rotation - vec4 z = vec4(1.0 + uColorShift, 2.0 + uColorShift, 3.0 + uColorShift, 0.0); - vec4 o = z; - - float a = 0.5; - float t = uTime; - float i = 0.0; - - // Original: for (float a=.5, t=iTime, i; ++i<19.; o+=...) v=..., u+=... - // Rewritten: float i counts 1..18 (pre-increment in condition); - // ++t and a+=.03 moved to explicit statements preserving order; - // comma-operator body split into sequential statements. - vec2 v = res; - - for (int iter = 0; iter < 18; iter++) { - i += 1.0; // pre-increment: matches ++i in original condition - - // ++t happens inside the v= expression (pre-increment before the rhs is evaluated) - t += 1.0; - a += 0.03; - - v = cos(t - 7.0 * u * pow(a, i)) - 5.0 * u; - - // mat2 from explicit components avoids mat2(vec4) constructor rejection on ANGLE/Mesa - // vec4(scalar) - vec4 ensures float-vec4 subtraction is unambiguous - vec4 r = cos(vec4(i + 0.02 * t) - z.wxzw * 11.0); - u *= mat2(r.x, r.y, r.z, r.w); - - // If black/NaN artifacts appear, replace tanh with stanh (smoothstep-tanh approximation) - u += tanh(40.0 * dot(u, u) * cos(100.0 * u.yx + t)) / 200.0 - + 0.2 * a * u - + cos(4.0 / exp(dot(o, o) / 100.0) + t) / 300.0; - - // for-increment: accumulate glow using the freshly updated v and u - o += (1.0 + cos(z + t)) - / length((1.0 + i * dot(v, v)) - * sin(1.5 * u / (0.5 - dot(u, u)) - 9.0 * u.yx + t)); - } - - // Tonemapping: multiply numerator/denominator by o to avoid float/vec4 division - // Equivalent to: 25.6 / (min(o,13.) + 164./o) - o = 25.6 * o / (min(o, 13.0) * o + 164.0) - dot(u, u) / 250.0; - - fragColor = vec4(o); -} diff --git a/src/components/themes/zappy/index.tsx b/src/components/themes/zappy/index.tsx deleted file mode 100644 index 111aacf..0000000 --- a/src/components/themes/zappy/index.tsx +++ /dev/null @@ -1,59 +0,0 @@ -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: 'ZappyTheme', - 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 zoom = computed(() => themeStore.getThemeInput('zoom')?.value as number || 0.2) - const colorShift = computed(() => themeStore.getThemeInput('color-shift')?.value as number || 0.0) - - 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.zoom) { - gl.programInfo.uniforms.zoom = gl.ctx.getUniformLocation(gl.program as WebGLProgram, 'uZoom') - gl.programInfo.uniforms.colorShift = gl.ctx.getUniformLocation(gl.program as WebGLProgram, 'uColorShift') - } - - gl.pxratio = pxratio.value - gl.time += animationSpeed.value / 500 - - gl.ctx.uniform1f(gl.programInfo.uniforms.time, gl.time) - gl.ctx.uniform1f(gl.programInfo.uniforms.zoom, zoom.value) - gl.ctx.uniform1f(gl.programInfo.uniforms.colorShift, colorShift.value) - } - } - ) - render.running = true - }) - - onBeforeUnmount(() => { - render.running = false - }) - - return () => ( -
- -
- ) - } -}) diff --git a/src/components/themes/zappy/vertex.glsl b/src/components/themes/zappy/vertex.glsl deleted file mode 100644 index ab6ebcd..0000000 --- a/src/components/themes/zappy/vertex.glsl +++ /dev/null @@ -1,6 +0,0 @@ -#version 300 es -in vec2 aVertexPosition; - -void main() { - gl_Position = vec4(aVertexPosition, 0.0, 1.0); -} diff --git a/vite.config.ts b/vite.config.ts index 0aee206..db09160 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -33,6 +33,9 @@ export default defineConfig({ '@': fileURLToPath(new URL('./src', import.meta.url)) } }, + optimizeDeps: { + exclude: ['vividgl'] + }, css: { preprocessorOptions: { stylus: {}