feat: add lens flare effect on bright light sources (#109)
Implements procedural lens flare using Three.js Lensflare addon. Canvas-generated textures (no external assets) are attached to overheadLight and meterLight. Three.js handles occlusion automatically — flares fade when the source is behind geometry and intensify when the camera looks directly toward the light. Fixes #109
This commit is contained in:
41
app.js
41
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();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user