Feature: Drugged Out Flow visualizer
This commit is contained in:
parent
887d3970ab
commit
070f4d6e01
206
party-stage/src/visualizers/DruggedOutFlowVisualizer.js
Normal file
206
party-stage/src/visualizers/DruggedOutFlowVisualizer.js
Normal file
@ -0,0 +1,206 @@
|
|||||||
|
import { VisualizerInterface } from './VisualizerInterface.js';
|
||||||
|
|
||||||
|
const fragmentShader = `
|
||||||
|
uniform float u_time;
|
||||||
|
uniform float u_beat;
|
||||||
|
uniform float u_opacity;
|
||||||
|
uniform vec2 u_resolution;
|
||||||
|
uniform vec3 u_colors[16];
|
||||||
|
uniform int u_colorCount;
|
||||||
|
uniform float u_seed;
|
||||||
|
varying vec2 vUv;
|
||||||
|
|
||||||
|
// --- Utility Functions ---
|
||||||
|
float hash(float n) { return fract(sin(n) * 43758.5453123); }
|
||||||
|
vec2 rotate(vec2 p, float a) {
|
||||||
|
float s = sin(a);
|
||||||
|
float c = cos(a);
|
||||||
|
return mat2(c, -s, s, c) * p;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- SDFs for Shapes ---
|
||||||
|
float sdCircle(vec2 p, float r) {
|
||||||
|
return length(p) - r;
|
||||||
|
}
|
||||||
|
|
||||||
|
float sdStar(vec2 p, float r, float spikes) {
|
||||||
|
float an = atan(p.y, p.x) / (2.0 * 3.14159265359);
|
||||||
|
an = fract(an * spikes);
|
||||||
|
float r2 = r * mix(0.6, 1.0, step(0.5, an));
|
||||||
|
return length(p) - r2;
|
||||||
|
}
|
||||||
|
|
||||||
|
float sdSmiley(vec2 p, float r, float t, float id) {
|
||||||
|
float d = sdCircle(p, r);
|
||||||
|
|
||||||
|
// Eyes: Bigger and higher. Animate blinking.
|
||||||
|
float eyeBlink = smoothstep(0.9, 0.95, sin(t * 0.5 + id * 1.2));
|
||||||
|
float eyeR = 0.18 * r * (1.0 - eyeBlink * 0.9);
|
||||||
|
vec2 eyePos = vec2(0.3, 0.35) * r;
|
||||||
|
d = max(d, -sdCircle(vec2(abs(p.x) - eyePos.x, p.y - eyePos.y), eyeR));
|
||||||
|
|
||||||
|
// Mouth: Changes shape (Smile/Surprise/Small mouth)
|
||||||
|
float mouthType = sin(t * 1.2 + id);
|
||||||
|
if (mouthType > 0.6) { // Surprise
|
||||||
|
d = max(d, -sdCircle(p - vec2(0.0, -0.2) * r, 0.15 * r));
|
||||||
|
} else if (mouthType > -0.6) { // Smile
|
||||||
|
float mouth = sdCircle(p - vec2(0.0, -0.1) * r, 0.45 * r);
|
||||||
|
mouth = max(mouth, -(p.y - (0.05 + 0.1 * u_beat) * r)); // Open more on beat
|
||||||
|
d = max(d, -mouth);
|
||||||
|
} else { // Small mouth
|
||||||
|
d = max(d, -sdCircle(p - vec2(0.0, -0.25) * r, 0.08 * r));
|
||||||
|
}
|
||||||
|
|
||||||
|
return d;
|
||||||
|
}
|
||||||
|
|
||||||
|
float distToSegment(vec2 p, vec2 a, vec2 b) {
|
||||||
|
vec2 pa = p - a, ba = b - a;
|
||||||
|
float h = clamp(dot(pa, ba) / dot(ba, ba), 0.0, 1.0);
|
||||||
|
return length(pa - ba * h);
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Main Shader ---
|
||||||
|
void main() {
|
||||||
|
// LED Grid Setup
|
||||||
|
float ledCountX = u_resolution.x;
|
||||||
|
float ledCountY = u_resolution.y;
|
||||||
|
vec2 gridUV = vec2(vUv.x * ledCountX, vUv.y * ledCountY);
|
||||||
|
vec2 gridDeriv = fwidth(gridUV);
|
||||||
|
float gridDensity = max(gridDeriv.x, gridDeriv.y);
|
||||||
|
float blurFactor = smoothstep(0.3, 0.8, gridDensity);
|
||||||
|
vec2 cell = fract(gridUV);
|
||||||
|
vec2 uv = mix((floor(gridUV) + 0.5) / vec2(ledCountX, ledCountY), vUv, blurFactor);
|
||||||
|
|
||||||
|
float distMask = distance(cell, vec2(0.5));
|
||||||
|
float edgeSoftness = clamp(gridDensity * 1.5, 0.0, 0.5);
|
||||||
|
float mask = 1.0 - smoothstep(0.35 - edgeSoftness, 0.45 + edgeSoftness, distMask);
|
||||||
|
mask = mix(mask, 1.0, blurFactor);
|
||||||
|
|
||||||
|
// Aspect correction
|
||||||
|
vec2 p = (uv - 0.5);
|
||||||
|
p.x *= u_resolution.x / u_resolution.y;
|
||||||
|
|
||||||
|
float t = u_time * 0.2 + u_seed;
|
||||||
|
float beat = u_beat;
|
||||||
|
|
||||||
|
// --- Background: Swirling psychedelic colors ---
|
||||||
|
vec3 colorA = vec3(0.05, 0.0, 0.1);
|
||||||
|
vec3 colorB = vec3(0.1, 0.0, 0.2);
|
||||||
|
if (u_colorCount >= 2) {
|
||||||
|
colorA = u_colors[0] * 0.1;
|
||||||
|
colorB = u_colors[int(mod(1.0, float(u_colorCount)))] * 0.15;
|
||||||
|
}
|
||||||
|
|
||||||
|
float bgNoise = sin(p.x * 3.0 + t) * cos(p.y * 3.0 - t * 0.7) + sin(length(p) * 4.0 - t);
|
||||||
|
vec3 finalColor = mix(colorA, colorB, bgNoise * 0.5 + 0.5);
|
||||||
|
finalColor += colorA * 0.05 * beat; // Flash background on beat
|
||||||
|
|
||||||
|
// --- Scene Switching Logic ---
|
||||||
|
float sceneDuration = 15.0;
|
||||||
|
float sceneIndex = floor(t / sceneDuration);
|
||||||
|
float currentSceneSeed = u_seed + sceneIndex * 100.0;
|
||||||
|
|
||||||
|
// --- "Splitting Eyes" Distortion ---
|
||||||
|
vec2 distortionCenter = vec2(sin(t * 0.3 + currentSceneSeed) * 0.5, cos(t * 0.4 + currentSceneSeed * 1.1) * 0.5);
|
||||||
|
float distortionRadius = 0.35 + beat * 0.15;
|
||||||
|
float distToDistortion = length(p - distortionCenter);
|
||||||
|
|
||||||
|
vec2 distortedP = p;
|
||||||
|
if (distToDistortion < distortionRadius) {
|
||||||
|
float strength = smoothstep(distortionRadius, 0.0, distToDistortion);
|
||||||
|
distortedP += normalize(p - distortionCenter) * strength * (0.15 + beat * 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Pre-calculate Lasers for interaction ---
|
||||||
|
vec2 lp1[3], lp2[3];
|
||||||
|
for (int j = 0; j < 3; j++) {
|
||||||
|
float laserSeed = currentSceneSeed + float(j) * 789.0;
|
||||||
|
lp1[j] = vec2(sin(t * 0.4 + laserSeed) * 0.8, cos(t * 0.3 + laserSeed * 1.1) * 0.5);
|
||||||
|
lp2[j] = vec2(sin(t * 0.5 + laserSeed * 1.5) * 0.8, cos(t * 0.4 + laserSeed * 1.7) * 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Render Symbols ---
|
||||||
|
int numSymbols = 8 + int(mod(currentSceneSeed, 16.0));
|
||||||
|
for (int i = 0; i < 24; i++) {
|
||||||
|
if (i >= numSymbols) break;
|
||||||
|
float fi = float(i);
|
||||||
|
float symbolSeed = currentSceneSeed + fi * 123.456;
|
||||||
|
|
||||||
|
// Motion: Orbital + attraction to distortion center
|
||||||
|
float speed = 0.3 + hash(symbolSeed) * 0.4;
|
||||||
|
vec2 basePos = vec2(
|
||||||
|
sin(t * speed + symbolSeed) * 0.8,
|
||||||
|
cos(t * speed * 0.8 + symbolSeed * 1.4) * 0.5
|
||||||
|
);
|
||||||
|
vec2 pos = mix(basePos, distortionCenter, 0.15 + beat * 0.1);
|
||||||
|
|
||||||
|
vec2 sp = distortedP - pos;
|
||||||
|
sp = rotate(sp, t * (0.1 + fract(symbolSeed * 0.7)));
|
||||||
|
|
||||||
|
float d;
|
||||||
|
float size = 0.06 + fract(symbolSeed * 0.3) * 0.04 + beat * 0.02;
|
||||||
|
|
||||||
|
// Determine symbol type based on seed
|
||||||
|
if (mod(fi + sceneIndex, 3.0) == 0.0) { // Stars
|
||||||
|
d = sdStar(sp, size, 5.0 + floor(hash(symbolSeed * 1.2) * 3.0));
|
||||||
|
} else if (mod(fi + sceneIndex, 3.0) == 1.0) { // Smilies
|
||||||
|
d = sdSmiley(sp, size, u_time, fi);
|
||||||
|
} else { // Circles
|
||||||
|
d = sdCircle(sp, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Interaction: Check proximity to lasers
|
||||||
|
float laserHit = 0.0;
|
||||||
|
for (int j = 0; j < 3; j++) {
|
||||||
|
float distToLaser = distToSegment(pos, lp1[j], lp2[j]);
|
||||||
|
laserHit = max(laserHit, smoothstep(0.15, 0.0, distToLaser));
|
||||||
|
}
|
||||||
|
|
||||||
|
float intensity = smoothstep(0.01, 0.0, d);
|
||||||
|
|
||||||
|
// Pick a color
|
||||||
|
vec3 symbolColor = vec3(1.0);
|
||||||
|
if (u_colorCount > 0) {
|
||||||
|
int colorIdx = int(mod(fi + floor(t * 0.1), float(u_colorCount)));
|
||||||
|
symbolColor = u_colors[colorIdx];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Interaction: Laser impact
|
||||||
|
symbolColor = mix(symbolColor, vec3(1.0), laserHit * 0.8);
|
||||||
|
float finalGlow = (0.5 + beat * 0.5 + laserHit * 2.0);
|
||||||
|
|
||||||
|
finalColor = mix(finalColor, symbolColor, intensity * clamp(0.7 + beat * 0.3 + laserHit, 0.0, 1.0));
|
||||||
|
|
||||||
|
// Add a slight glow/aura
|
||||||
|
finalColor += symbolColor * (1.0 - smoothstep(0.0, size * 2.5, abs(d))) * 0.15 * finalGlow;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Draw Laser Beams ---
|
||||||
|
for (int i = 0; i < 3; i++) {
|
||||||
|
float distToBeam = distToSegment(distortedP, lp1[i], lp2[i]);
|
||||||
|
float beamWidth = 0.008 + beat * 0.005;
|
||||||
|
float beamIntensity = smoothstep(beamWidth, 0.0, distToBeam);
|
||||||
|
|
||||||
|
vec3 beamColor = vec3(1.0);
|
||||||
|
if (u_colorCount > 0) {
|
||||||
|
int colorIdx = int(mod(float(i) + floor(t * 0.2), float(u_colorCount)));
|
||||||
|
beamColor = u_colors[colorIdx];
|
||||||
|
}
|
||||||
|
|
||||||
|
finalColor += beamColor * beamIntensity * (0.6 + beat * 0.4);
|
||||||
|
}
|
||||||
|
|
||||||
|
gl_FragColor = vec4(finalColor, mask * u_opacity);
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export class DruggedOutFlowVisualizer extends VisualizerInterface {
|
||||||
|
getName() {
|
||||||
|
return "Drugged Out Flow";
|
||||||
|
}
|
||||||
|
|
||||||
|
getFragmentShader() {
|
||||||
|
return fragmentShader;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -2,12 +2,14 @@ import { ClassicVisualizer } from './ClassicVisualizer.js';
|
|||||||
import { NebulaVisualizer } from './NebulaVisualizer.js';
|
import { NebulaVisualizer } from './NebulaVisualizer.js';
|
||||||
import { FloatingShapesVisualizer } from './FloatingShapesVisualizer.js';
|
import { FloatingShapesVisualizer } from './FloatingShapesVisualizer.js';
|
||||||
import { SynthwaveRunVisualizer } from './SynthwaveRunVisualizer.js';
|
import { SynthwaveRunVisualizer } from './SynthwaveRunVisualizer.js';
|
||||||
|
import { DruggedOutFlowVisualizer } from './DruggedOutFlowVisualizer.js';
|
||||||
|
|
||||||
export const visualizerLibrary = [
|
export const visualizerLibrary = [
|
||||||
new ClassicVisualizer(),
|
new ClassicVisualizer(),
|
||||||
new NebulaVisualizer(),
|
new NebulaVisualizer(),
|
||||||
new FloatingShapesVisualizer(),
|
new FloatingShapesVisualizer(),
|
||||||
new SynthwaveRunVisualizer(),
|
new SynthwaveRunVisualizer(),
|
||||||
|
new DruggedOutFlowVisualizer(),
|
||||||
];
|
];
|
||||||
|
|
||||||
export default visualizerLibrary;
|
export default visualizerLibrary;
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user