forked from BRETT/BRETT
Initial Commit
This commit is contained in:
@@ -0,0 +1,194 @@
|
||||
<script setup lang="ts">
|
||||
import { hexToRgbTuple, shadeHex } from '@/lib/color'
|
||||
|
||||
// Portiert aus Vue Bits (DavidHDev/vue-bits), von three.js auf ogl umgeschrieben,
|
||||
// vereinfacht auf color-Prop
|
||||
const props = withDefaults(defineProps<{ color?: string }>(), {
|
||||
color: '#6366f1',
|
||||
})
|
||||
|
||||
const VERT = `
|
||||
attribute vec2 uv;
|
||||
attribute vec2 position;
|
||||
varying vec2 vUv;
|
||||
void main() {
|
||||
vUv = uv;
|
||||
gl_Position = vec4(position, 0.0, 1.0);
|
||||
}
|
||||
`
|
||||
|
||||
const FRAG = `
|
||||
precision highp float;
|
||||
|
||||
varying vec2 vUv;
|
||||
uniform float uTime;
|
||||
uniform float uSpeed;
|
||||
uniform vec3 uColor;
|
||||
uniform vec3 uBackgroundColor;
|
||||
uniform float uCurvature;
|
||||
uniform float uScanlineStrength;
|
||||
uniform float uScanlineFrequency;
|
||||
uniform float uWaveAmplitude;
|
||||
uniform float uWaveFrequency;
|
||||
uniform float uBloom;
|
||||
uniform float uBloomRadius;
|
||||
uniform float uNoise;
|
||||
uniform float uVignette;
|
||||
uniform float uBrightness;
|
||||
uniform float uPixelation;
|
||||
uniform float uRgbShift;
|
||||
uniform vec2 uPointer;
|
||||
uniform float uMouseStrength;
|
||||
uniform vec2 uCardRes;
|
||||
uniform float uRadius;
|
||||
|
||||
float hash21(vec2 p) {
|
||||
p = fract(p * vec2(123.34, 456.21));
|
||||
p += dot(p, p + 45.32);
|
||||
return fract(p.x * p.y);
|
||||
}
|
||||
|
||||
vec2 crtCurve(vec2 uv, float radius) {
|
||||
vec2 p = (uv - 0.5) * 2.0;
|
||||
float safeRadius = max(radius, 1.415);
|
||||
float cornerScale = safeRadius / sqrt(max(safeRadius * safeRadius - 2.0, 0.001));
|
||||
p = safeRadius * p / sqrt(max(safeRadius * safeRadius - dot(p, p), 0.001));
|
||||
p /= cornerScale;
|
||||
return p * 0.5 + 0.5;
|
||||
}
|
||||
|
||||
float referencePlasma(vec2 uv, float t) {
|
||||
float frequencyScale = max(uWaveFrequency / 2.2, 0.001);
|
||||
uv = (uv - 0.5) * frequencyScale + 0.5;
|
||||
|
||||
float scanline = 0.5 - 0.5 * cos(uv.y * 3.14159265 * uScanlineFrequency);
|
||||
scanline = mix(1.0, scanline, uScanlineStrength);
|
||||
|
||||
// Pixelkonsistente Zellen (~16px) statt fixem 80x24-Raster,
|
||||
// damit das CRT-Pixeln in flachen Containern nicht staucht
|
||||
vec2 cells = max(round(uCardRes / 16.0), vec2(4.0));
|
||||
uv *= cells;
|
||||
uv = ceil(uv);
|
||||
uv /= cells;
|
||||
|
||||
float amplitude = uWaveAmplitude / 0.28;
|
||||
float field = 0.0;
|
||||
field += 0.7 * sin(0.5 * uv.x + t / 5.0);
|
||||
field += 3.0 * sin(1.6 * uv.y + t / 5.0);
|
||||
field += sin(10.0 * (uv.y * sin(t / 2.0) + uv.x * cos(t / 5.0)) + t / 2.0);
|
||||
|
||||
float cx = uv.x + 0.5 * sin(t / 2.0);
|
||||
float cy = uv.y + 0.5 * cos(t / 4.0);
|
||||
field += 0.4 * sin(sqrt(100.0 * cx * cx + 100.0 * cy * cy + 1.0) + t);
|
||||
field += 0.9 * sin(sqrt(75.0 * cx * cx + 25.0 * cy * cy + 1.0) + t);
|
||||
field -= 1.4 * sin(sqrt(256.0 * cx * cx + 25.0 * cy * cy + 1.0) + t);
|
||||
field += 0.3 * sin(0.5 * uv.y + uv.x + sin(t));
|
||||
|
||||
return scanline * floor(3.0 * (0.5 + 0.499 * sin(field * amplitude))) / 3.0;
|
||||
}
|
||||
|
||||
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 uv = vUv;
|
||||
if (uPixelation > 1.001) {
|
||||
vec2 cells = max(uCardRes / uPixelation, vec2(1.0));
|
||||
uv = (floor(uv * cells) + 0.5) / cells;
|
||||
}
|
||||
|
||||
float curveRadius = 1.1 + 0.42 / max(uCurvature, 0.001);
|
||||
curveRadius *= exp(-uPointer.y * uMouseStrength * 0.4);
|
||||
vec2 curvedUv = crtCurve(uv, curveRadius);
|
||||
curvedUv.x -= uPointer.x * uMouseStrength * 0.035;
|
||||
|
||||
float t = uTime * uSpeed;
|
||||
float signal = referencePlasma(curvedUv, t);
|
||||
float radius = 0.01 * uBloomRadius;
|
||||
float glow = signal * 0.2;
|
||||
glow += referencePlasma(curvedUv + vec2(radius, 0.0), t) * 0.12;
|
||||
glow += referencePlasma(curvedUv - vec2(radius, 0.0), t) * 0.12;
|
||||
glow += referencePlasma(curvedUv + vec2(0.0, radius), t) * 0.12;
|
||||
glow += referencePlasma(curvedUv - vec2(0.0, radius), t) * 0.12;
|
||||
glow += referencePlasma(curvedUv + vec2(radius), t) * 0.08;
|
||||
glow += referencePlasma(curvedUv - vec2(radius), t) * 0.08;
|
||||
glow += referencePlasma(curvedUv + vec2(radius, -radius), t) * 0.08;
|
||||
glow += referencePlasma(curvedUv + vec2(-radius, radius), t) * 0.08;
|
||||
|
||||
float redSignal = referencePlasma(curvedUv + vec2(uRgbShift, 0.0), t);
|
||||
float blueSignal = referencePlasma(curvedUv - vec2(uRgbShift, 0.0), t);
|
||||
vec3 channelSignal = vec3(redSignal, signal, blueSignal);
|
||||
vec3 waveColor = uColor * (0.3 + signal * 0.7 + glow * uBloom * 0.65);
|
||||
waveColor += (channelSignal - signal) * 0.42;
|
||||
|
||||
float edge = clamp(1.0 - dot(vUv - 0.5, vUv - 0.5) * 2.0, 0.0, 1.0);
|
||||
float edgeFade = mix(1.0, smoothstep(0.0, 1.0, edge), uVignette);
|
||||
float waveMask = clamp(signal * 0.82 + glow * 0.52, 0.0, 1.0) * edgeFade;
|
||||
|
||||
waveColor = max(waveColor * uBrightness, vec3(0.0));
|
||||
float a = clamp(waveMask, 0.0, 1.0) * cardMask(gl_FragCoord.xy);
|
||||
vec3 col = waveColor;
|
||||
if (a > 0.01) {
|
||||
float grain = (hash21(gl_FragCoord.xy + vec2(fract(uTime) * 173.0)) - 0.5) * uNoise;
|
||||
col = max(col + grain, vec3(0.0));
|
||||
}
|
||||
gl_FragColor = vec4(col * a, a);
|
||||
}
|
||||
`
|
||||
|
||||
const ctnDom = ref<HTMLDivElement | null>(null)
|
||||
|
||||
let elapsed = 0
|
||||
let lastT = 0
|
||||
|
||||
useCardShader(ctnDom, {
|
||||
vertex: VERT,
|
||||
fragment: FRAG,
|
||||
mouse: true,
|
||||
uniforms: {
|
||||
uTime: { value: 0 },
|
||||
uSpeed: { value: 0.5 },
|
||||
uColor: { value: new Float32Array(hexToRgbTuple(props.color)) },
|
||||
uBackgroundColor: { value: new Float32Array(hexToRgbTuple(shadeHex(props.color, -0.93))) },
|
||||
uCurvature: { value: 0.25 },
|
||||
uScanlineStrength: { value: 0.25 },
|
||||
uScanlineFrequency: { value: 200 },
|
||||
uWaveAmplitude: { value: 0.3 },
|
||||
uWaveFrequency: { value: 2.5 },
|
||||
uBloom: { value: 1.5 },
|
||||
uBloomRadius: { value: 1 },
|
||||
uNoise: { value: 0.1 },
|
||||
uVignette: { value: 0 },
|
||||
uBrightness: { value: 1.25 },
|
||||
uPixelation: { value: 1 },
|
||||
uRgbShift: { value: 0.015 },
|
||||
uPointer: { value: new Float32Array([0, 0]) },
|
||||
uMouseStrength: { value: 0.5 },
|
||||
},
|
||||
color: () => props.color,
|
||||
colorUniforms: { uColor: 0, uBackgroundColor: -0.93 },
|
||||
frame: ({ t, program, mouse }) => {
|
||||
// fps-Cap ~30 wie im Original (CRT-Flackern)
|
||||
const u = program.uniforms
|
||||
const delta = Math.min((t - lastT) * 0.001, 0.1)
|
||||
lastT = t
|
||||
elapsed += delta
|
||||
u.uTime.value = elapsed
|
||||
const p = u.uPointer.value as Float32Array
|
||||
p[0] = (mouse.x * 2 - 1) * mouse.active
|
||||
p[1] = (mouse.y * 2 - 1) * mouse.active
|
||||
// Scanline-Dichte in Pixel-Pitch (eine Linie alle ~6 Buffer-Pixel)
|
||||
// statt fixer Zeilenzahl — flache Karten sonst subpixel-gestaucht
|
||||
const res = u.uCardRes!.value as Float32Array
|
||||
u.uScanlineFrequency!.value = res[1] / 6
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div ref="ctnDom" class="relative size-full overflow-hidden rounded-[inherit]" />
|
||||
</template>
|
||||
Reference in New Issue
Block a user