forked from BRETT/BRETT
153 lines
4.3 KiB
Vue
153 lines
4.3 KiB
Vue
<script setup lang="ts">
|
|
import { hexToRgbTuple, shadeHex } from '@/lib/color'
|
|
|
|
// Portiert aus Vue Bits (DavidHDev/vue-bits), vereinfacht auf color-Prop,
|
|
// Blur-Post-Pass entfernt (blur: 0 war Standard)
|
|
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 uWaveDepth;
|
|
uniform float uZoom;
|
|
uniform float uDensity;
|
|
uniform float uSpread;
|
|
uniform float uStepSize;
|
|
uniform float uGlow;
|
|
uniform float uExposure;
|
|
uniform float uColorShift;
|
|
uniform float uContrast;
|
|
uniform float uBrightness;
|
|
uniform float uOpacity;
|
|
uniform float uSteps;
|
|
uniform vec3 uColor1;
|
|
uniform vec3 uColor2;
|
|
uniform vec3 uColor3;
|
|
uniform vec2 uMouse;
|
|
uniform float uMouseStrength;
|
|
uniform float uMouseRadius;
|
|
uniform float uMouseActive;
|
|
uniform float uGrainIntensity;
|
|
uniform vec2 uCardRes;
|
|
uniform float uRadius;
|
|
out vec4 fragColor;
|
|
|
|
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() {
|
|
vec2 frag = gl_FragCoord.xy;
|
|
float zoom = max(uZoom, 0.05);
|
|
// Pixelkonsistent: Referenzhöhe 180px, damit die Zellen in flachen
|
|
// Containern ihre reale Pixelgröße behalten statt zu stauchen
|
|
vec2 ndc = (2.0 * frag - uCardRes.xy) / 180.0;
|
|
vec2 dir = ndc * (0.5 / zoom);
|
|
|
|
vec2 mouseNdc = uMouse * (uCardRes / 360.0);
|
|
float mr = max(uMouseRadius, 0.01);
|
|
vec2 md = ndc - mouseNdc;
|
|
float dent = exp(-dot(md, md) / (mr * mr)) * (3.0 * uMouseStrength * uMouseActive);
|
|
|
|
float travel = sin(iTime * uSpeed) * uWaveDepth;
|
|
float density = max(uDensity, 1.0);
|
|
float spread = clamp(uSpread, 0.05, 0.6);
|
|
float stepSize = max(uStepSize, 0.0005);
|
|
float glowGain = max(uGlow, 0.0);
|
|
|
|
vec3 tOffset = vec3(0.0, dent, travel);
|
|
vec3 p = vec3(0.0);
|
|
float s = 0.0;
|
|
float glow = 0.0;
|
|
|
|
for (int i = 0; i < 64; i++) {
|
|
if (float(i) >= uSteps) break;
|
|
p += vec3(dir * s, s);
|
|
vec3 q = p + tOffset;
|
|
s += density - length(q.xz) + length(ceil(q).xy);
|
|
s = stepSize + abs(s) * spread;
|
|
glow += glowGain / s;
|
|
}
|
|
|
|
float e = glow / max(uExposure, 1.0);
|
|
float shimmer = 0.5 + 0.5 * dot(cos(iTime * uColorShift + p), vec3(0.3333));
|
|
float v = tanh(e * uBrightness * mix(0.7, 1.05, shimmer));
|
|
v = clamp((v - 0.5) * uContrast + 0.5, 0.0, 1.0);
|
|
|
|
vec3 col = mix(uColor1, uColor2, smoothstep(0.0, 0.55, v));
|
|
col = mix(col, uColor3, smoothstep(0.55, 1.0, v));
|
|
col *= v;
|
|
|
|
float a = clamp(v, 0.0, 1.0) * uOpacity;
|
|
vec3 outRgb = col;
|
|
if (a > 0.01) {
|
|
float gv = (fract(sin(dot(gl_FragCoord.xy, vec2(12.9898, 78.233)) + iTime) * 43758.5453) - 0.5) * uGrainIntensity;
|
|
outRgb = clamp(outRgb + gv, 0.0, 1.0);
|
|
a = clamp(a + gv, 0.0, 1.0);
|
|
}
|
|
|
|
a *= cardMask(frag);
|
|
fragColor = vec4(outRgb * a, a);
|
|
}
|
|
`
|
|
|
|
const ctnDom = ref<HTMLDivElement | null>(null)
|
|
|
|
useCardShader(ctnDom, {
|
|
vertex: VERT,
|
|
fragment: FRAG,
|
|
mouse: true,
|
|
uniforms: {
|
|
iTime: { value: 0 },
|
|
uSpeed: { value: 0.7 },
|
|
uWaveDepth: { value: 1 },
|
|
uZoom: { value: 1.3 },
|
|
uDensity: { value: 10.0 },
|
|
uSpread: { value: 0.3 },
|
|
uStepSize: { value: 0.002 },
|
|
uGlow: { value: 1.0 },
|
|
uExposure: { value: 2700 },
|
|
uColorShift: { value: 0 },
|
|
uContrast: { value: 1 },
|
|
uBrightness: { value: 1.0 },
|
|
uOpacity: { value: 1.0 },
|
|
uSteps: { value: 32 },
|
|
uColor1: { value: new Float32Array(hexToRgbTuple(shadeHex(props.color, -0.35))) },
|
|
uColor2: { value: new Float32Array(hexToRgbTuple(props.color)) },
|
|
uColor3: { value: new Float32Array([1, 1, 1]) },
|
|
uMouse: { value: new Float32Array([0, 0]) },
|
|
uMouseStrength: { value: 0.1 },
|
|
uMouseRadius: { value: 0.35 },
|
|
uMouseActive: { value: 0 },
|
|
uGrainIntensity: { value: 0.05 },
|
|
},
|
|
color: () => props.color,
|
|
colorUniforms: { uColor1: -0.35, 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 * 2 - 1
|
|
m[1] = mouse.y * 2 - 1
|
|
u.uMouseActive.value = mouse.active
|
|
},
|
|
})
|
|
</script>
|
|
|
|
<template>
|
|
<div ref="ctnDom" class="relative size-full overflow-hidden rounded-[inherit]" />
|
|
</template>
|