forked from BRETT/BRETT
182 lines
5.3 KiB
Vue
182 lines
5.3 KiB
Vue
<script setup lang="ts">
|
|
import { hexToRgbTuple, shadeHex } from '@/lib/color'
|
|
|
|
// Portiert aus Vue Bits (DavidHDev/vue-bits), vereinfacht auf color-Prop
|
|
const props = withDefaults(defineProps<{ color?: string }>(), {
|
|
color: '#6366f1',
|
|
})
|
|
|
|
const VERT = `#version 300 es
|
|
in vec2 position;
|
|
void main() {
|
|
gl_Position = vec4(position, 0.0, 1.0);
|
|
}
|
|
`
|
|
|
|
const FRAG = `#version 300 es
|
|
precision highp float;
|
|
uniform float iTime;
|
|
uniform float uSpeed;
|
|
uniform float uSweepSpeed;
|
|
uniform float uSweepWidth;
|
|
uniform float uSweepFalloff;
|
|
uniform float uScale;
|
|
uniform float uFrequency;
|
|
uniform float uRipple;
|
|
uniform float uBandDensity;
|
|
uniform float uLineSharpness;
|
|
uniform float uGlow;
|
|
uniform float uColorSpread;
|
|
uniform float uBrightness;
|
|
uniform float uContrast;
|
|
uniform float uSoftness;
|
|
uniform float uVignette;
|
|
uniform float uOpacity;
|
|
uniform float uScanline;
|
|
uniform float uGrain;
|
|
uniform float uGrainIntensity;
|
|
uniform vec2 uMouse;
|
|
uniform float uMouseRadius;
|
|
uniform float uMouseStrength;
|
|
uniform float uMouseActive;
|
|
uniform vec3 uColor1;
|
|
uniform vec3 uColor2;
|
|
uniform vec3 uColor3;
|
|
uniform vec2 uCardRes;
|
|
uniform float uRadius;
|
|
out vec4 fragColor;
|
|
|
|
const float TAU = 6.2831853;
|
|
|
|
float signalField(vec2 p, float t) {
|
|
float w = sin(p.x * 1.3 + t * 0.7);
|
|
w += sin(p.y * 1.7 - t * 0.52) * 0.8;
|
|
w += sin((p.x + p.y) * 0.9 + t * 0.91) * 0.6;
|
|
w += sin((p.x - p.y) * 1.53 - t * 0.63) * 0.42;
|
|
return w * 0.35;
|
|
}
|
|
|
|
vec3 palette(float f) {
|
|
f = clamp(f, 0.0, 1.0);
|
|
f = pow(f, uContrast);
|
|
vec3 c = mix(uColor1, uColor2, smoothstep(0.08, 0.6, f));
|
|
return mix(c, uColor3, smoothstep(0.68, 1.0, f));
|
|
}
|
|
|
|
float scanBand(float x, float aa, float sharp) {
|
|
float v = mix(0.5, 0.5 + 0.5 * cos(x * TAU), aa);
|
|
return pow(v, sharp);
|
|
}
|
|
|
|
float cardMask(vec2 fc) {
|
|
vec2 p = fc - uCardRes * 0.5;
|
|
vec2 hb = uCardRes * 0.5 - vec2(uRadius);
|
|
float rd = length(max(abs(p) - hb, 0.0)) - uRadius;
|
|
return 1.0 - smoothstep(-0.75, 0.75, rd);
|
|
}
|
|
|
|
void main() {
|
|
// Pixelkonsistent: auf Referenzhöhe 180px normieren,
|
|
// damit Bänder/Wellen in flachen Containern nicht stauchen
|
|
vec2 uv0 = (gl_FragCoord.xy * 2.0 - uCardRes.xy) / 180.0;
|
|
vec2 p = uv0 / max(uScale, 0.001);
|
|
|
|
float t = iTime * uSpeed;
|
|
|
|
vec2 mUv = (uMouse * 2.0 - 1.0) * (uCardRes / 360.0);
|
|
vec2 md = uv0 - mUv;
|
|
float r = max(uMouseRadius, 0.001);
|
|
float mouseBoost = exp(-dot(md, md) / (r * r)) * uMouseStrength * uMouseActive;
|
|
|
|
float axis = p.y;
|
|
|
|
float sig = signalField(p * uFrequency, t);
|
|
float coord = axis + sig * uRipple;
|
|
|
|
float phase = coord / max(uSweepWidth, 0.05) - t * uSweepSpeed;
|
|
float sweep = pow(0.5 + 0.5 * cos(phase * TAU), max(uSweepFalloff, 0.1));
|
|
|
|
float lc = coord * uBandDensity;
|
|
float aa = 1.0 / (1.0 + uSoftness * fwidth(lc) * 3.0);
|
|
aa = clamp(aa * (1.0 + mouseBoost * 0.6), 0.0, 1.0);
|
|
|
|
float bodyBase = clamp(0.5 + 0.5 * sig, 0.0, 1.0);
|
|
float body = bodyBase * bodyBase * uGlow * sweep;
|
|
|
|
float sharp = max(uLineSharpness, 0.1);
|
|
float split = uColorSpread * 0.16;
|
|
float fr = clamp(scanBand(lc + split, aa, sharp) * sweep + body, 0.0, 1.0);
|
|
float fg = clamp(scanBand(lc, aa, sharp) * sweep + body, 0.0, 1.0);
|
|
float fb = clamp(scanBand(lc - split, aa, sharp) * sweep + body, 0.0, 1.0);
|
|
|
|
vec3 col = vec3(palette(fr).r, palette(fg).g, palette(fb).b);
|
|
|
|
float inten = (fr + fg + fb) * 0.3333333 * uBrightness;
|
|
inten *= 1.0 + mouseBoost * 0.9;
|
|
inten *= 1.0 - 0.18 * (0.5 + 0.5 * cos(gl_FragCoord.y * 1.7));
|
|
|
|
if (inten > 0.01) {
|
|
float g = fract(sin(dot(gl_FragCoord.xy, vec2(12.9898, 78.233)) + iTime) * 43758.5453);
|
|
inten += (g - 0.5) * uGrainIntensity;
|
|
}
|
|
|
|
inten *= clamp(1.0 - uVignette * smoothstep(0.55, 1.65, length(uv0)), 0.0, 1.0);
|
|
inten = clamp(inten, 0.0, 1.0);
|
|
|
|
float a = clamp(inten * uOpacity, 0.0, 1.0) * cardMask(gl_FragCoord.xy);
|
|
fragColor = vec4(clamp(col, 0.0, 1.0) * a, a);
|
|
}
|
|
`
|
|
|
|
const ctnDom = ref<HTMLDivElement | null>(null)
|
|
|
|
useCardShader(ctnDom, {
|
|
vertex: VERT,
|
|
fragment: FRAG,
|
|
mouse: true,
|
|
uniforms: {
|
|
iTime: { value: 0 },
|
|
uSpeed: { value: 0.5 },
|
|
uSweepSpeed: { value: 0.25 },
|
|
uSweepWidth: { value: 1.6 },
|
|
uSweepFalloff: { value: 6 },
|
|
uScale: { value: 1.5 },
|
|
uFrequency: { value: 2 },
|
|
uRipple: { value: 0.22 },
|
|
uBandDensity: { value: 11 },
|
|
uLineSharpness: { value: 5.5 },
|
|
uGlow: { value: 0.22 },
|
|
uColorSpread: { value: 0.7 },
|
|
uBrightness: { value: 1.0 },
|
|
uContrast: { value: 1.15 },
|
|
uSoftness: { value: 1.4 },
|
|
uVignette: { value: 0.45 },
|
|
uOpacity: { value: 1.0 },
|
|
uScanline: { value: 1.0 },
|
|
uGrain: { value: 1.0 },
|
|
uGrainIntensity: { value: 0.05 },
|
|
uMouse: { value: new Float32Array([0.5, 0.5]) },
|
|
uMouseRadius: { value: 0.5 },
|
|
uMouseStrength: { value: 0.5 },
|
|
uMouseActive: { value: 0 },
|
|
uColor1: { value: new Float32Array(hexToRgbTuple(shadeHex(props.color, -0.55))) },
|
|
uColor2: { value: new Float32Array(hexToRgbTuple(props.color)) },
|
|
uColor3: { value: new Float32Array([1, 1, 1]) },
|
|
},
|
|
color: () => props.color,
|
|
colorUniforms: { uColor1: -0.55, uColor2: 0 },
|
|
frame: ({ t, program, mouse }) => {
|
|
const u = program.uniforms
|
|
u.iTime.value = t * 0.001
|
|
const m = u.uMouse.value as Float32Array
|
|
m[0] = mouse.x
|
|
m[1] = mouse.y
|
|
u.uMouseActive.value = mouse.active
|
|
},
|
|
})
|
|
</script>
|
|
|
|
<template>
|
|
<div ref="ctnDom" class="relative size-full overflow-hidden rounded-[inherit]" />
|
|
</template>
|