Files
BRETT/app/components/effects/EffectAcidSquares.vue
T
2026-10-03 20:07:49 +02:00

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>