diff --git a/app.js b/app.js index adb18c0..fc313b4 100644 --- a/app.js +++ b/app.js @@ -4,6 +4,7 @@ import { EffectComposer } from 'three/addons/postprocessing/EffectComposer.js'; import { RenderPass } from 'three/addons/postprocessing/RenderPass.js'; import { BokehPass } from 'three/addons/postprocessing/BokehPass.js'; import { LoadingManager } from 'three'; +import { Lensflare, LensflareElement } from 'three/addons/objects/Lensflare.js'; // === COLOR PALETTE === const NEXUS = { @@ -575,6 +576,46 @@ async function loadSovereigntyStatus() { loadSovereigntyStatus(); +// === LENS FLARE === +// Procedural canvas textures — no external assets required. +// LensflareElement at distance=0 sits at the light; elements at distance>0 +// streak along the lens axis, producing the classic cinematic look. +// Three.js handles occlusion automatically: flares fade when the source is +// behind geometry and brighten when the camera looks directly toward the light. + +function createFlareTexture(size, inner, mid, outer) { + const c = document.createElement('canvas'); + c.width = c.height = size; + const ctx = c.getContext('2d'); + const cx = size / 2; + const g = ctx.createRadialGradient(cx, cx, 0, cx, cx, cx); + g.addColorStop(0, inner); + g.addColorStop(0.25, mid); + g.addColorStop(1, outer); + ctx.fillStyle = g; + ctx.fillRect(0, 0, size, size); + return new THREE.CanvasTexture(c); +} + +const flareTex0 = createFlareTexture(256, 'rgba(255,255,240,1)', 'rgba(120,160,255,0.5)', 'rgba(0,0,0,0)'); +const flareTex1 = createFlareTexture(128, 'rgba(180,200,255,0.7)', 'rgba(60,100,220,0.25)', 'rgba(0,0,0,0)'); +const flareTex2 = createFlareTexture(64, 'rgba(255,210,110,0.6)', 'rgba(200,80,20,0.2)', 'rgba(0,0,0,0)'); + +function attachLensFlare(light, baseColor, mainSize) { + const lf = new Lensflare(); + lf.addElement(new LensflareElement(flareTex0, mainSize, 0, baseColor)); + lf.addElement(new LensflareElement(flareTex1, mainSize * 0.28, 0.55)); + lf.addElement(new LensflareElement(flareTex2, mainSize * 0.18, 0.82)); + lf.addElement(new LensflareElement(flareTex1, mainSize * 0.12, 1.1)); + lf.addElement(new LensflareElement(flareTex2, mainSize * 0.08, 1.35)); + light.add(lf); +} + +// Overhead point-light — primary flare (cool blue-white) +attachLensFlare(overheadLight, new THREE.Color(0x8899bb), 350); +// Sovereignty meter glow — secondary flare (color driven by score, starts green) +attachLensFlare(meterLight, new THREE.Color(0x00ff88), 180); + // === ANIMATION LOOP === const clock = new THREE.Clock();