mirror of
https://github.com/DavidHDev/vue-bits.git
synced 2026-03-07 22:49:31 -07:00
1 line
12 KiB
JSON
1 line
12 KiB
JSON
{"name":"Dither","title":"Dither","description":"Retro dithered noise shader background.","type":"registry:component","add":"when-added","files":[{"type":"registry:component","role":"file","content":"<template>\n <div ref=\"containerRef\" class=\"w-full h-full absolute top-0 left-0\" />\n</template>\n\n<script setup lang=\"ts\">\nimport { onMounted, onUnmounted, watch, useTemplateRef } from 'vue';\nimport { Renderer, Program, Mesh, Triangle, Color } from 'ogl';\nimport type { OGLRenderingContext } from 'ogl';\n\ninterface DitherProps {\n waveSpeed?: number;\n waveFrequency?: number;\n waveAmplitude?: number;\n waveColor?: [number, number, number];\n colorNum?: number;\n pixelSize?: number;\n disableAnimation?: boolean;\n enableMouseInteraction?: boolean;\n mouseRadius?: number;\n}\n\nconst props = withDefaults(defineProps<DitherProps>(), {\n waveSpeed: 0.05,\n waveFrequency: 3,\n waveAmplitude: 0.3,\n waveColor: () => [0.5, 0.5, 0.5] as [number, number, number],\n colorNum: 4,\n pixelSize: 2,\n disableAnimation: false,\n enableMouseInteraction: true,\n mouseRadius: 1\n});\n\nconst containerRef = useTemplateRef<HTMLDivElement>('containerRef');\n\nlet renderer: Renderer | null = null;\nlet gl: OGLRenderingContext | null = null;\nlet program: Program | null = null;\nlet mesh: Mesh | null = null;\nlet animationId: number | null = null;\nlet currentMouse = [0, 0];\nlet targetMouse = [0, 0];\n\nconst vertexShader = `\nattribute vec2 position;\nattribute vec2 uv;\nvarying vec2 vUv;\nvoid main() {\n vUv = uv;\n gl_Position = vec4(position, 0.0, 1.0);\n}\n`;\n\nconst fragmentShader = `\nprecision highp float;\n\nuniform float time;\nuniform vec2 resolution;\nuniform float waveSpeed;\nuniform float waveFrequency;\nuniform float waveAmplitude;\nuniform vec3 waveColor;\nuniform vec2 mousePos;\nuniform int enableMouseInteraction;\nuniform float mouseRadius;\nuniform float colorNum;\nuniform float pixelSize;\n\nvarying vec2 vUv;\n\nvec4 mod289(vec4 x) { return x - floor(x * (1.0/289.0)) * 289.0; }\nvec4 permute(vec4 x) { return mod289(((x * 34.0) + 1.0) * x); }\nvec4 taylorInvSqrt(vec4 r) { return 1.79284291400159 - 0.85373472095314 * r; }\nvec2 fade(vec2 t) { return t*t*t*(t*(t*6.0-15.0)+10.0); }\n\nfloat cnoise(vec2 P) {\n vec4 Pi = floor(P.xyxy) + vec4(0.0,0.0,1.0,1.0);\n vec4 Pf = fract(P.xyxy) - vec4(0.0,0.0,1.0,1.0);\n Pi = mod289(Pi);\n vec4 ix = Pi.xzxz;\n vec4 iy = Pi.yyww;\n vec4 fx = Pf.xzxz;\n vec4 fy = Pf.yyww;\n vec4 i = permute(permute(ix) + iy);\n vec4 gx = fract(i * (1.0/41.0)) * 2.0 - 1.0;\n vec4 gy = abs(gx) - 0.5;\n vec4 tx = floor(gx + 0.5);\n gx = gx - tx;\n vec2 g00 = vec2(gx.x, gy.x);\n vec2 g10 = vec2(gx.y, gy.y);\n vec2 g01 = vec2(gx.z, gy.z);\n vec2 g11 = vec2(gx.w, gy.w);\n vec4 norm = taylorInvSqrt(vec4(dot(g00,g00), dot(g01,g01), dot(g10,g10), dot(g11,g11)));\n g00 *= norm.x; g01 *= norm.y; g10 *= norm.z; g11 *= norm.w;\n float n00 = dot(g00, vec2(fx.x, fy.x));\n float n10 = dot(g10, vec2(fx.y, fy.y));\n float n01 = dot(g01, vec2(fx.z, fy.z));\n float n11 = dot(g11, vec2(fx.w, fy.w));\n vec2 fade_xy = fade(Pf.xy);\n vec2 n_x = mix(vec2(n00, n01), vec2(n10, n11), fade_xy.x);\n return 2.3 * mix(n_x.x, n_x.y, fade_xy.y);\n}\n\nconst int OCTAVES = 8;\nfloat fbm(vec2 p) {\n float value = 0.0;\n float amp = 1.0;\n float freq = waveFrequency;\n for (int i = 0; i < OCTAVES; i++) {\n value += amp * abs(cnoise(p));\n p *= freq;\n amp *= waveAmplitude;\n }\n return value;\n}\n\nfloat pattern(vec2 p) {\n vec2 p2 = p - time * waveSpeed;\n return fbm(p - fbm(p + fbm(p2)));\n}\n\nfloat getBayerValue(int x, int y) {\n if (y == 0) {\n if (x == 0) return 0.0/64.0;\n if (x == 1) return 48.0/64.0;\n if (x == 2) return 12.0/64.0;\n if (x == 3) return 60.0/64.0;\n if (x == 4) return 3.0/64.0;\n if (x == 5) return 51.0/64.0;\n if (x == 6) return 15.0/64.0;\n if (x == 7) return 63.0/64.0;\n } else if (y == 1) {\n if (x == 0) return 32.0/64.0;\n if (x == 1) return 16.0/64.0;\n if (x == 2) return 44.0/64.0;\n if (x == 3) return 28.0/64.0;\n if (x == 4) return 35.0/64.0;\n if (x == 5) return 19.0/64.0;\n if (x == 6) return 47.0/64.0;\n if (x == 7) return 31.0/64.0;\n } else if (y == 2) {\n if (x == 0) return 8.0/64.0;\n if (x == 1) return 56.0/64.0;\n if (x == 2) return 4.0/64.0;\n if (x == 3) return 52.0/64.0;\n if (x == 4) return 11.0/64.0;\n if (x == 5) return 59.0/64.0;\n if (x == 6) return 7.0/64.0;\n if (x == 7) return 55.0/64.0;\n } else if (y == 3) {\n if (x == 0) return 40.0/64.0;\n if (x == 1) return 24.0/64.0;\n if (x == 2) return 36.0/64.0;\n if (x == 3) return 20.0/64.0;\n if (x == 4) return 43.0/64.0;\n if (x == 5) return 27.0/64.0;\n if (x == 6) return 39.0/64.0;\n if (x == 7) return 23.0/64.0;\n } else if (y == 4) {\n if (x == 0) return 2.0/64.0;\n if (x == 1) return 50.0/64.0;\n if (x == 2) return 14.0/64.0;\n if (x == 3) return 62.0/64.0;\n if (x == 4) return 1.0/64.0;\n if (x == 5) return 49.0/64.0;\n if (x == 6) return 13.0/64.0;\n if (x == 7) return 61.0/64.0;\n } else if (y == 5) {\n if (x == 0) return 34.0/64.0;\n if (x == 1) return 18.0/64.0;\n if (x == 2) return 46.0/64.0;\n if (x == 3) return 30.0/64.0;\n if (x == 4) return 33.0/64.0;\n if (x == 5) return 17.0/64.0;\n if (x == 6) return 45.0/64.0;\n if (x == 7) return 29.0/64.0;\n } else if (y == 6) {\n if (x == 0) return 10.0/64.0;\n if (x == 1) return 58.0/64.0;\n if (x == 2) return 6.0/64.0;\n if (x == 3) return 54.0/64.0;\n if (x == 4) return 9.0/64.0;\n if (x == 5) return 57.0/64.0;\n if (x == 6) return 5.0/64.0;\n if (x == 7) return 53.0/64.0;\n } else if (y == 7) {\n if (x == 0) return 42.0/64.0;\n if (x == 1) return 26.0/64.0;\n if (x == 2) return 38.0/64.0;\n if (x == 3) return 22.0/64.0;\n if (x == 4) return 41.0/64.0;\n if (x == 5) return 25.0/64.0;\n if (x == 6) return 37.0/64.0;\n if (x == 7) return 21.0/64.0;\n }\n return 0.0;\n}\n\nvec3 dither(vec2 uv, vec3 color) {\n vec2 scaledCoord = floor(uv * resolution / pixelSize);\n int x = int(mod(scaledCoord.x, 8.0));\n int y = int(mod(scaledCoord.y, 8.0));\n float threshold = getBayerValue(x, y) - 0.25;\n float step = 1.0 / (colorNum - 1.0);\n color += threshold * step;\n float bias = 0.2;\n color = clamp(color - bias, 0.0, 1.0);\n return floor(color * (colorNum - 1.0) + 0.5) / (colorNum - 1.0);\n}\n\nvoid main() {\n vec2 uv = gl_FragCoord.xy / resolution.xy;\n vec2 centeredUv = uv - 0.5;\n centeredUv.x *= resolution.x / resolution.y;\n \n float f = pattern(centeredUv);\n \n if (enableMouseInteraction == 1) {\n vec2 mouseNDC = (mousePos / resolution - 0.5) * vec2(1.0, -1.0);\n mouseNDC.x *= resolution.x / resolution.y;\n float dist = length(centeredUv - mouseNDC);\n float effect = 1.0 - smoothstep(0.0, mouseRadius, dist);\n f -= 0.5 * effect;\n }\n \n vec3 col = mix(vec3(0.0), waveColor, f);\n col = dither(uv, col);\n \n gl_FragColor = vec4(col, 1.0);\n}\n`;\n\nconst resize = () => {\n if (!containerRef.value || !renderer || !program) return;\n\n const container = containerRef.value;\n const { clientWidth, clientHeight } = container;\n renderer.setSize(clientWidth, clientHeight);\n program.uniforms.resolution.value[0] = clientWidth;\n program.uniforms.resolution.value[1] = clientHeight;\n};\n\nconst handleMouseMove = (e: MouseEvent) => {\n if (!containerRef.value || !gl) return;\n\n const rect = containerRef.value.getBoundingClientRect();\n\n const normalizedX = (e.clientX - rect.left) / rect.width;\n const normalizedY = (e.clientY - rect.top) / rect.height;\n\n const x = normalizedX * gl.canvas.width;\n const y = normalizedY * gl.canvas.height;\n\n targetMouse = [x, y];\n};\n\nconst handleMouseLeave = () => {\n if (!gl) return;\n targetMouse = [gl.canvas.width / 2, gl.canvas.height / 2];\n};\n\nconst update = (t: number) => {\n if (!program || !renderer || !mesh) return;\n\n if (props.enableMouseInteraction) {\n const smoothing = 0.05;\n currentMouse[0] += smoothing * (targetMouse[0] - currentMouse[0]);\n currentMouse[1] += smoothing * (targetMouse[1] - currentMouse[1]);\n program.uniforms.mousePos.value[0] = currentMouse[0];\n program.uniforms.mousePos.value[1] = currentMouse[1];\n } else {\n if (gl) {\n program.uniforms.mousePos.value[0] = gl.canvas.width / 2;\n program.uniforms.mousePos.value[1] = gl.canvas.height / 2;\n }\n }\n\n if (!props.disableAnimation) {\n program.uniforms.time.value = t * 0.001;\n }\n\n program.uniforms.waveSpeed.value = props.waveSpeed;\n program.uniforms.waveFrequency.value = props.waveFrequency;\n program.uniforms.waveAmplitude.value = props.waveAmplitude;\n program.uniforms.waveColor.value.r = props.waveColor[0];\n program.uniforms.waveColor.value.g = props.waveColor[1];\n program.uniforms.waveColor.value.b = props.waveColor[2];\n program.uniforms.enableMouseInteraction.value = props.enableMouseInteraction ? 1 : 0;\n program.uniforms.mouseRadius.value = props.mouseRadius;\n program.uniforms.colorNum.value = props.colorNum;\n program.uniforms.pixelSize.value = props.pixelSize;\n\n renderer.render({ scene: mesh });\n animationId = requestAnimationFrame(update);\n};\n\nconst initializeScene = () => {\n if (!containerRef.value) return;\n\n cleanup();\n\n const container = containerRef.value;\n renderer = new Renderer({ alpha: true });\n gl = renderer.gl;\n gl.clearColor(0, 0, 0, 0);\n gl.enable(gl.BLEND);\n gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);\n\n const geometry = new Triangle(gl);\n program = new Program(gl, {\n vertex: vertexShader,\n fragment: fragmentShader,\n uniforms: {\n time: { value: 0 },\n resolution: { value: new Float32Array([gl.canvas.width, gl.canvas.height]) },\n waveSpeed: { value: props.waveSpeed },\n waveFrequency: { value: props.waveFrequency },\n waveAmplitude: { value: props.waveAmplitude },\n waveColor: { value: new Color(...props.waveColor) },\n mousePos: { value: new Float32Array([gl.canvas.width / 2, gl.canvas.height / 2]) },\n enableMouseInteraction: { value: props.enableMouseInteraction ? 1 : 0 },\n mouseRadius: { value: props.mouseRadius },\n colorNum: { value: props.colorNum },\n pixelSize: { value: props.pixelSize }\n }\n });\n\n mesh = new Mesh(gl, { geometry, program });\n\n const canvas = gl.canvas as HTMLCanvasElement;\n canvas.style.width = '100%';\n canvas.style.height = '100%';\n canvas.style.display = 'block';\n\n container.appendChild(canvas);\n\n window.addEventListener('resize', resize);\n if (props.enableMouseInteraction) {\n container.addEventListener('mousemove', handleMouseMove);\n container.addEventListener('mouseleave', handleMouseLeave);\n }\n\n resize();\n animationId = requestAnimationFrame(update);\n};\n\nconst cleanup = () => {\n if (animationId) {\n cancelAnimationFrame(animationId);\n animationId = null;\n }\n\n window.removeEventListener('resize', resize);\n\n if (containerRef.value) {\n containerRef.value.removeEventListener('mousemove', handleMouseMove);\n containerRef.value.removeEventListener('mouseleave', handleMouseLeave);\n\n const canvas = containerRef.value.querySelector('canvas');\n if (canvas) {\n containerRef.value.removeChild(canvas);\n }\n }\n\n if (gl) {\n gl.getExtension('WEBGL_lose_context')?.loseContext();\n }\n\n renderer = null;\n gl = null;\n program = null;\n mesh = null;\n currentMouse = [0, 0];\n targetMouse = [0, 0];\n};\n\nonMounted(() => {\n initializeScene();\n});\n\nonUnmounted(() => {\n cleanup();\n});\n\nwatch(\n () => props,\n () => {\n initializeScene();\n },\n { deep: true }\n);\n</script>\n","path":"Dither/Dither.vue","_imports_":[],"registryDependencies":[],"dependencies":[],"devDependencies":[]}],"registryDependencies":[],"dependencies":[{"ecosystem":"js","name":"ogl","version":"^1.0.11"}],"devDependencies":[],"categories":["Backgrounds"]} |