- Hugo static site at root: config, content (home/about/resume/blog + 5 projects), data/resume.yaml, custom layouts (gpu background, resume renderer, projects list), dark theme with canvas behind content - gpu-bg/: extracted WebGPU/WebGL2 spiral engine as a Vite sub-package; slim bootstrap drops router/overlay/text renderer; fixed uniforms (scene0/dim0); self-contained engine.js - archive/: retired Vite SPA preserved (router, overlay, data, engine/text orphaned) - 3-stage Dockerfile (node -> hugo extended/dart-sass -> nginx), static nginx config - DEPLOYMENT.md handover + README rewrite; pre-hugo rollback tag
109 lines
3.4 KiB
WebGPU Shading Language
109 lines
3.4 KiB
WebGPU Shading Language
struct Uniforms {
|
||
resolution: vec2<f32>,
|
||
time: f32,
|
||
scene: f32,
|
||
mouse: vec2<f32>,
|
||
date: vec4<f32>,
|
||
sampleRate: vec4<f32>,
|
||
stemMultipliers: array<vec4<f32>, 8>,
|
||
};
|
||
|
||
@group(0) @binding(0) var<uniform> u: Uniforms;
|
||
|
||
struct VsOut {
|
||
@builtin(position) pos: vec4<f32>,
|
||
@location(0) uv: vec2<f32>,
|
||
};
|
||
|
||
@vertex
|
||
fn vs(@builtin(vertex_index) i: u32) -> VsOut {
|
||
let p = array<vec2<f32>, 3>(
|
||
vec2<f32>(-1.0, -3.0),
|
||
vec2<f32>(-1.0, 1.0),
|
||
vec2<f32>(3.0, 1.0),
|
||
);
|
||
let clip = p[i];
|
||
var out: VsOut;
|
||
out.pos = vec4<f32>(clip, 0.0, 1.0);
|
||
out.uv = clip;
|
||
return out;
|
||
}
|
||
|
||
@fragment
|
||
fn fs(in: VsOut) -> @location(0) vec4<f32> {
|
||
let res = u.resolution;
|
||
let fragCoord = (in.uv * vec2<f32>(0.5) + vec2<f32>(0.5)) * res;
|
||
|
||
var uv = (fragCoord - 0.5 * res) / res.y;
|
||
|
||
let slowTime = u.time * 0.2;
|
||
|
||
let bgGrey = vec3<f32>(0.11, 0.11, 0.12);
|
||
let cardGrey = vec3<f32>(0.30, 0.32, 0.36);
|
||
let coreWhite = vec3<f32>(0.90, 0.95, 0.90);
|
||
|
||
// ---------- palette keyed by scene ----------
|
||
let scene0 = vec3<f32>(0.20, 0.75, 0.20); // HERO – emerald
|
||
let scene1 = vec3<f32>(0.85, 0.55, 0.20); // ABOUT – amber
|
||
let scene2 = vec3<f32>(0.25, 0.60, 0.95); // PROJECTS – cobalt
|
||
let scene3 = vec3<f32>(0.90, 0.35, 0.55); // CONTACT – rose
|
||
|
||
let sFloor = i32(clamp(floor(u.scene), 0.0, 3.0));
|
||
let sCeil = i32(clamp(ceil(u.scene), 0.0, 3.0));
|
||
var c0 = scene0;
|
||
c0 = select(c0, scene1, sFloor == 1);
|
||
c0 = select(c0, scene2, sFloor == 2);
|
||
c0 = select(c0, scene3, sFloor == 3);
|
||
var c1 = scene0;
|
||
c1 = select(c1, scene1, sCeil == 1);
|
||
c1 = select(c1, scene2, sCeil == 2);
|
||
c1 = select(c1, scene3, sCeil == 3);
|
||
let tBlend = u.scene - floor(u.scene);
|
||
let sceneAccent = mix(c0, c1, tBlend * tBlend * (3.0 - 2.0 * tBlend));
|
||
|
||
// ---------- mouse-reactive distortion ----------
|
||
let mousePos = (u.mouse - 0.5 * res) / res.y;
|
||
let distMouse = length(uv - mousePos);
|
||
let influence = exp(-distMouse * 7.0);
|
||
uv += (mousePos - uv) * influence * 0.12;
|
||
|
||
let angle = atan2(uv.y, uv.x);
|
||
let radius = length(uv);
|
||
|
||
let baseSpiral = angle + (radius * 8.0) - slowTime;
|
||
let ripple = sin(radius * 15.0 - (u.time * 0.5)) * 0.08;
|
||
let distortedSpiral = baseSpiral + ripple;
|
||
|
||
let armCount = 2.0;
|
||
let rChannel = smoothstep(0.05, 0.55, abs(sin((distortedSpiral + 0.04) * armCount)));
|
||
let gChannel = smoothstep(0.05, 0.55, abs(sin(distortedSpiral * armCount)));
|
||
let bChannel = smoothstep(0.05, 0.55, abs(sin((distortedSpiral - 0.04) * armCount)));
|
||
let patternChannels = vec3<f32>(rChannel, gChannel, bChannel);
|
||
|
||
let coreNeon = vec3<f32>(0.02 / (patternChannels + 0.025));
|
||
let spiralColor = mix(cardGrey, sceneAccent, patternChannels.g);
|
||
let primarySpiral = coreNeon * spiralColor;
|
||
|
||
let ringWave = sin(radius * 12.0 - (u.time * 1.5));
|
||
let sharpRing = smoothstep(0.85, 0.95, ringWave);
|
||
let ringMask = sharpRing * smoothstep(0.7, 0.1, radius);
|
||
let secondaryRings = cardGrey * ringMask * 0.4;
|
||
|
||
let vignette = smoothstep(0.95, 0.4, radius);
|
||
|
||
var finalColor = bgGrey + secondaryRings;
|
||
finalColor = mix(finalColor, primarySpiral, 0.85);
|
||
|
||
let centerLight = 0.015 / (radius + 0.02);
|
||
finalColor += centerLight * coreWhite;
|
||
|
||
// soft cursor glow
|
||
finalColor += exp(-distMouse * 4.0) * 0.03 * sceneAccent;
|
||
|
||
// ---------- dim when not on the home page (u.date.x controls it) ----------
|
||
let dim = u.date.x;
|
||
finalColor = mix(finalColor, vec3<f32>(0.0), dim);
|
||
|
||
return vec4<f32>(finalColor * vignette, 1.0);
|
||
}
|