Files
vue-bits/public/r/Galaxy.json
David Haz e621971723 jsrepo v3
2025-12-15 23:50:24 +02:00

1 line
11 KiB
JSON

{"name":"Galaxy","title":"Galaxy","description":"Parallax realistic starfield with pointer interactions.","type":"registry:component","add":"when-added","files":[{"type":"registry:component","role":"file","content":"<script setup lang=\"ts\">\nimport { Renderer, Program, Mesh, Color, Triangle } from 'ogl';\nimport { onMounted, onUnmounted, ref, useTemplateRef, watch } from 'vue';\n\nconst vertexShader = `\nattribute vec2 uv;\nattribute vec2 position;\n\nvarying vec2 vUv;\n\nvoid main() {\n vUv = uv;\n gl_Position = vec4(position, 0, 1);\n}\n`;\n\nconst fragmentShader = `\nprecision highp float;\n\nuniform float uTime;\nuniform vec3 uResolution;\nuniform vec2 uFocal;\nuniform vec2 uRotation;\nuniform float uStarSpeed;\nuniform float uDensity;\nuniform float uHueShift;\nuniform float uSpeed;\nuniform vec2 uMouse;\nuniform float uGlowIntensity;\nuniform float uSaturation;\nuniform bool uMouseRepulsion;\nuniform float uTwinkleIntensity;\nuniform float uRotationSpeed;\nuniform float uRepulsionStrength;\nuniform float uMouseActiveFactor;\nuniform float uAutoCenterRepulsion;\nuniform bool uTransparent;\n\nvarying vec2 vUv;\n\n#define NUM_LAYER 4.0\n#define STAR_COLOR_CUTOFF 0.2\n#define MAT45 mat2(0.7071, -0.7071, 0.7071, 0.7071)\n#define PERIOD 3.0\n\nfloat Hash21(vec2 p) {\n p = fract(p * vec2(123.34, 456.21));\n p += dot(p, p + 45.32);\n return fract(p.x * p.y);\n}\n\nfloat tri(float x) {\n return abs(fract(x) * 2.0 - 1.0);\n}\n\nfloat tris(float x) {\n float t = fract(x);\n return 1.0 - smoothstep(0.0, 1.0, abs(2.0 * t - 1.0));\n}\n\nfloat trisn(float x) {\n float t = fract(x);\n return 2.0 * (1.0 - smoothstep(0.0, 1.0, abs(2.0 * t - 1.0))) - 1.0;\n}\n\nvec3 hsv2rgb(vec3 c) {\n vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);\n vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);\n return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);\n}\n\nfloat Star(vec2 uv, float flare) {\n float d = length(uv);\n float m = (0.05 * uGlowIntensity) / d;\n float rays = smoothstep(0.0, 1.0, 1.0 - abs(uv.x * uv.y * 1000.0));\n m += rays * flare * uGlowIntensity;\n uv *= MAT45;\n rays = smoothstep(0.0, 1.0, 1.0 - abs(uv.x * uv.y * 1000.0));\n m += rays * 0.3 * flare * uGlowIntensity;\n m *= smoothstep(1.0, 0.2, d);\n return m;\n}\n\nvec3 StarLayer(vec2 uv) {\n vec3 col = vec3(0.0);\n\n vec2 gv = fract(uv) - 0.5; \n vec2 id = floor(uv);\n\n for (int y = -1; y <= 1; y++) {\n for (int x = -1; x <= 1; x++) {\n vec2 offset = vec2(float(x), float(y));\n vec2 si = id + vec2(float(x), float(y));\n float seed = Hash21(si);\n float size = fract(seed * 345.32);\n float glossLocal = tri(uStarSpeed / (PERIOD * seed + 1.0));\n float flareSize = smoothstep(0.9, 1.0, size) * glossLocal;\n\n float red = smoothstep(STAR_COLOR_CUTOFF, 1.0, Hash21(si + 1.0)) + STAR_COLOR_CUTOFF;\n float blu = smoothstep(STAR_COLOR_CUTOFF, 1.0, Hash21(si + 3.0)) + STAR_COLOR_CUTOFF;\n float grn = min(red, blu) * seed;\n vec3 base = vec3(red, grn, blu);\n \n float hue = atan(base.g - base.r, base.b - base.r) / (2.0 * 3.14159) + 0.5;\n hue = fract(hue + uHueShift / 360.0);\n float sat = length(base - vec3(dot(base, vec3(0.299, 0.587, 0.114)))) * uSaturation;\n float val = max(max(base.r, base.g), base.b);\n base = hsv2rgb(vec3(hue, sat, val));\n\n vec2 pad = vec2(tris(seed * 34.0 + uTime * uSpeed / 10.0), tris(seed * 38.0 + uTime * uSpeed / 30.0)) - 0.5;\n\n float star = Star(gv - offset - pad, flareSize);\n vec3 color = base;\n\n float twinkle = trisn(uTime * uSpeed + seed * 6.2831) * 0.5 + 1.0;\n twinkle = mix(1.0, twinkle, uTwinkleIntensity);\n star *= twinkle;\n \n col += star * size * color;\n }\n }\n\n return col;\n}\n\nvoid main() {\n vec2 focalPx = uFocal * uResolution.xy;\n vec2 uv = (vUv * uResolution.xy - focalPx) / uResolution.y;\n\n vec2 mouseNorm = uMouse - vec2(0.5);\n \n if (uAutoCenterRepulsion > 0.0) {\n vec2 centerUV = vec2(0.0, 0.0); // Center in UV space\n float centerDist = length(uv - centerUV);\n vec2 repulsion = normalize(uv - centerUV) * (uAutoCenterRepulsion / (centerDist + 0.1));\n uv += repulsion * 0.05;\n } else if (uMouseRepulsion) {\n vec2 mousePosUV = (uMouse * uResolution.xy - focalPx) / uResolution.y;\n float mouseDist = length(uv - mousePosUV);\n vec2 repulsion = normalize(uv - mousePosUV) * (uRepulsionStrength / (mouseDist + 0.1));\n uv += repulsion * 0.05 * uMouseActiveFactor;\n } else {\n vec2 mouseOffset = mouseNorm * 0.1 * uMouseActiveFactor;\n uv += mouseOffset;\n }\n\n float autoRotAngle = uTime * uRotationSpeed;\n mat2 autoRot = mat2(cos(autoRotAngle), -sin(autoRotAngle), sin(autoRotAngle), cos(autoRotAngle));\n uv = autoRot * uv;\n\n uv = mat2(uRotation.x, -uRotation.y, uRotation.y, uRotation.x) * uv;\n\n vec3 col = vec3(0.0);\n\n for (float i = 0.0; i < 1.0; i += 1.0 / NUM_LAYER) {\n float depth = fract(i + uStarSpeed * uSpeed);\n float scale = mix(20.0 * uDensity, 0.5 * uDensity, depth);\n float fade = depth * smoothstep(1.0, 0.9, depth);\n col += StarLayer(uv * scale + i * 453.32) * fade;\n }\n\n if (uTransparent) {\n float alpha = length(col);\n alpha = smoothstep(0.0, 0.3, alpha); // Enhance contrast\n alpha = min(alpha, 1.0); // Clamp to maximum 1.0\n gl_FragColor = vec4(col, alpha);\n } else {\n gl_FragColor = vec4(col, 1.0);\n }\n}\n`;\n\ninterface GalaxyProps {\n focal?: [number, number];\n rotation?: [number, number];\n starSpeed?: number;\n density?: number;\n hueShift?: number;\n disableAnimation?: boolean;\n speed?: number;\n mouseInteraction?: boolean;\n glowIntensity?: number;\n saturation?: number;\n mouseRepulsion?: boolean;\n twinkleIntensity?: number;\n rotationSpeed?: number;\n repulsionStrength?: number;\n autoCenterRepulsion?: number;\n transparent?: boolean;\n}\n\nconst props = withDefaults(defineProps<GalaxyProps>(), {\n focal: () => [0.5, 0.5],\n rotation: () => [1.0, 0.0],\n starSpeed: 0.5,\n density: 1,\n hueShift: 140,\n disableAnimation: false,\n speed: 1.0,\n mouseInteraction: true,\n glowIntensity: 0.3,\n saturation: 0.0,\n mouseRepulsion: true,\n twinkleIntensity: 0.3,\n rotationSpeed: 0.1,\n repulsionStrength: 2,\n autoCenterRepulsion: 0,\n transparent: true\n});\n\nconst ctnDom = useTemplateRef('ctnDom');\nconst targetMousePos = ref({ x: 0.5, y: 0.5 });\nconst smoothMousePos = ref({ x: 0.5, y: 0.5 });\nconst targetMouseActive = ref(0.0);\nconst smoothMouseActive = ref(0.0);\n\nlet cleanup: (() => void) | null = null;\n\nconst setup = () => {\n if (!ctnDom.value) return;\n const ctn = ctnDom.value;\n const renderer = new Renderer({\n alpha: props.transparent,\n premultipliedAlpha: false\n });\n const gl = renderer.gl;\n\n if (props.transparent) {\n gl.enable(gl.BLEND);\n gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);\n gl.clearColor(0, 0, 0, 0);\n } else {\n gl.clearColor(0, 0, 0, 1);\n }\n\n let program: Program;\n\n function resize() {\n const scale = 1;\n renderer.setSize(ctn.offsetWidth * scale, ctn.offsetHeight * scale);\n if (program) {\n program.uniforms.uResolution.value = new Color(\n gl.canvas.width,\n gl.canvas.height,\n gl.canvas.width / gl.canvas.height\n );\n }\n }\n window.addEventListener('resize', resize, false);\n resize();\n\n const geometry = new Triangle(gl);\n program = new Program(gl, {\n vertex: vertexShader,\n fragment: fragmentShader,\n uniforms: {\n uTime: { value: 0 },\n uResolution: {\n value: new Color(gl.canvas.width, gl.canvas.height, gl.canvas.width / gl.canvas.height)\n },\n uFocal: { value: new Float32Array(props.focal) },\n uRotation: { value: new Float32Array(props.rotation) },\n uStarSpeed: { value: props.starSpeed },\n uDensity: { value: props.density },\n uHueShift: { value: props.hueShift },\n uSpeed: { value: props.speed },\n uMouse: {\n value: new Float32Array([smoothMousePos.value.x, smoothMousePos.value.y])\n },\n uGlowIntensity: { value: props.glowIntensity },\n uSaturation: { value: props.saturation },\n uMouseRepulsion: { value: props.mouseRepulsion },\n uTwinkleIntensity: { value: props.twinkleIntensity },\n uRotationSpeed: { value: props.rotationSpeed },\n uRepulsionStrength: { value: props.repulsionStrength },\n uMouseActiveFactor: { value: 0.0 },\n uAutoCenterRepulsion: { value: props.autoCenterRepulsion },\n uTransparent: { value: props.transparent }\n }\n });\n\n const mesh = new Mesh(gl, { geometry, program });\n let animateId: number;\n\n function update(t: number) {\n animateId = requestAnimationFrame(update);\n if (!props.disableAnimation) {\n program.uniforms.uTime.value = t * 0.001;\n program.uniforms.uStarSpeed.value = (t * 0.001 * props.starSpeed) / 10.0;\n }\n\n const lerpFactor = 0.05;\n smoothMousePos.value.x += (targetMousePos.value.x - smoothMousePos.value.x) * lerpFactor;\n smoothMousePos.value.y += (targetMousePos.value.y - smoothMousePos.value.y) * lerpFactor;\n\n smoothMouseActive.value += (targetMouseActive.value - smoothMouseActive.value) * lerpFactor;\n\n program.uniforms.uMouse.value[0] = smoothMousePos.value.x;\n program.uniforms.uMouse.value[1] = smoothMousePos.value.y;\n program.uniforms.uMouseActiveFactor.value = smoothMouseActive.value;\n\n renderer.render({ scene: mesh });\n }\n animateId = requestAnimationFrame(update);\n ctn.appendChild(gl.canvas);\n\n function handleMouseMove(e: MouseEvent) {\n const rect = ctn.getBoundingClientRect();\n const x = (e.clientX - rect.left) / rect.width;\n const y = 1.0 - (e.clientY - rect.top) / rect.height;\n targetMousePos.value = { x, y };\n targetMouseActive.value = 1.0;\n }\n\n function handleMouseLeave() {\n targetMouseActive.value = 0.0;\n }\n\n if (props.mouseInteraction) {\n ctn.addEventListener('mousemove', handleMouseMove);\n ctn.addEventListener('mouseleave', handleMouseLeave);\n }\n\n cleanup = () => {\n cancelAnimationFrame(animateId);\n window.removeEventListener('resize', resize);\n if (props.mouseInteraction) {\n ctn.removeEventListener('mousemove', handleMouseMove);\n ctn.removeEventListener('mouseleave', handleMouseLeave);\n }\n ctn.removeChild(gl.canvas);\n gl.getExtension('WEBGL_lose_context')?.loseContext();\n };\n};\n\nonMounted(() => {\n cleanup?.();\n setup();\n});\n\nonUnmounted(() => {\n cleanup?.();\n});\n\nwatch(\n () => props,\n () => {\n cleanup?.();\n setup();\n },\n { deep: true }\n);\n</script>\n\n<template>\n <div ref=\"ctnDom\" class=\"relative w-full h-full\" v-bind=\"$attrs\" />\n</template>\n","path":"Galaxy/Galaxy.vue","_imports_":[],"registryDependencies":[],"dependencies":[],"devDependencies":[]}],"registryDependencies":[],"dependencies":[{"ecosystem":"js","name":"ogl","version":"^1.0.11"}],"devDependencies":[],"categories":["Backgrounds"]}