mirror of
https://github.com/openfrontio/OpenFrontIO.git
synced 2026-06-21 09:10:42 +00:00
update webgl nuke effects (#3984)
## Description: Reworks the visual look of nuked tiles to read uniformly green (no more brown/black bleed-through), and moves the ember "particle" effect out of the border passes — where it lived as a storage-sharing hack — into the fallout system where it belongs. ## What changed visually - **Fresh fallout**: bright uniform bloom with a hint of flickering green particles dampened on fresh tiles, ramping up as heat decays (`particleFreshScale` controls the fresh-tile dampening). - **Stale fallout**: dark-green ground (was near-black charcoal), with full-strength flickering particles in dark-green ↔ light-green. - **Particles**: per-tile flicker is now de-synced (each tile pulses at its own rate, 0.4×–1.6× base speed) so the eye can't lock onto a global rhythm. - **No more brown/black pixels** in fallout zones. Two root causes were fixed: - The territory pass now renders stale-nuke ground for **all** fallout tiles, not just unowned ones — so an owned player's color can't show through where the bloom is dim/transparent. - The ember stamp (which fully replaced tile color with orange) is gone; particle render is now additive and color-tuned green. ## Architecture cleanup The ember effect was conceptually fallout-domain, but lived in `BorderComputePass` (writing intensity into `borderTex.g`) and `BorderStampPass` (stamping orange dots), just because the border pass already had an RGBA8 texture with a free G channel. Two consumers read from it (`BorderStampPass`, `FalloutLightPass`), and the per-tile flicker math used no border data at all. This PR relocates the math inline into the two passes that actually need it (`FalloutBloomPass.extract.frag.glsl` and `FalloutLightPass.fallout-light.frag.glsl`), drops the ember code from both border passes, and renames `mapOverlay.ember*` → `falloutBloom.particle*` so the settings live with their pass. Side benefits: - **Animation correctness**: the old setup only updated ember intensity when `BorderComputePass`'s dirty flag flipped (highlight change, relations update, etc.), so the supposed flicker was actually a frozen snapshot between border events. The new inline path runs every frame as intended. - **Slightly cheaper per-frame compute**: removed a per-dirty-event full-map writeback to `borderTex.g`; added a few cheap ALU ops (1 sin + 2 hashes) per fallout tile in shaders that were already running. Same texture memory. ## Other small changes - Renamed `mapOverlay.charcoal*` → `mapOverlay.staleNuke*` (charcoal was a misnomer now that the ground is green). - Added `staleNukeR/G/B` for the ground color (was hardcoded grey). - `intensityHot` bumped 0.6 → 1.8 for a brighter fresh-nuke glow. - Raised `railroad.railMinZoom` 2 → 4 and `railDetailZoom` 4 → 6 so rails pop in later (separate small commit). <img width="354" height="371" alt="Screenshot 2026-05-22 at 10 37 34 AM" src="https://github.com/user-attachments/assets/03b46c45-c617-41b3-b3e4-9934f064bfe1" /> <img width="335" height="358" alt="Screenshot 2026-05-22 at 10 37 43 AM" src="https://github.com/user-attachments/assets/af370b19-8f22-4694-9859-1ad52aa755a7" /> <img width="651" height="613" alt="Screenshot 2026-05-22 at 10 38 09 AM" src="https://github.com/user-attachments/assets/e06e5101-8529-49f6-b29a-ce0563eb52d6" /> ## Please complete the following: - [x] I have added screenshots for all UI updates - [x] I process any text displayed to the user through translateText() and I've added it to the en.json file - [x] I have added relevant tests to the test directory - [x] I confirm I have thoroughly tested these changes and take full responsibility for any bugs introduced ## Please put your Discord username so you can be contacted if a bug or regression is found: evan
This commit is contained in:
@@ -34,6 +34,17 @@ export interface RenderSettings {
|
||||
bloomB: number;
|
||||
bloomCoverage: number;
|
||||
heatDecayPerTick: number;
|
||||
particleColorDarkR: number;
|
||||
particleColorDarkG: number;
|
||||
particleColorDarkB: number;
|
||||
particleColorBrightR: number;
|
||||
particleColorBrightG: number;
|
||||
particleColorBrightB: number;
|
||||
particleThresholdUnowned: number;
|
||||
particleThresholdOwned: number;
|
||||
particleFlickerSpeed: number;
|
||||
particleStrength: number;
|
||||
particleFreshScale: number;
|
||||
};
|
||||
dayNight: {
|
||||
mode: "light" | "dark";
|
||||
@@ -55,19 +66,12 @@ export interface RenderSettings {
|
||||
mapOverlay: {
|
||||
trailAlpha: number;
|
||||
defenseCheckerDarken: number;
|
||||
charcoalBase: number;
|
||||
charcoalVariation: number;
|
||||
charcoalAlpha: number;
|
||||
emberThresholdUnowned: number;
|
||||
emberThresholdOwned: number;
|
||||
emberFlickerSpeed: number;
|
||||
emberColorDarkR: number;
|
||||
emberColorDarkG: number;
|
||||
emberColorDarkB: number;
|
||||
emberColorBrightR: number;
|
||||
emberColorBrightG: number;
|
||||
emberColorBrightB: number;
|
||||
emberStrengthUnowned: number;
|
||||
staleNukeBase: number;
|
||||
staleNukeVariation: number;
|
||||
staleNukeAlpha: number;
|
||||
staleNukeR: number;
|
||||
staleNukeG: number;
|
||||
staleNukeB: number;
|
||||
highlightBrighten: number;
|
||||
highlightFillBrighten: number;
|
||||
highlightThicken: number;
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* Draw order:
|
||||
* DATA SYNC: tile flush → heat update → border compute
|
||||
* BASE PASS (darkened by night): terrain → territory fill + fallout charcoal
|
||||
* BASE PASS (darkened by night): terrain → territory fill + stale-nuke ground
|
||||
* NIGHT COMPOSITE (optional): lightmap → scene × (ambient + lightmap)
|
||||
* FULL BRIGHTNESS (always): borders → railroads → ground units → structures →
|
||||
* structure levels → bars → bloom → trails → missiles → fx → conquest → names
|
||||
@@ -342,13 +342,13 @@ export class GPURenderer {
|
||||
this.settings,
|
||||
);
|
||||
|
||||
// --- Fallout light (needs tileTex, borderTex, heatManager) ---
|
||||
// --- Fallout light (needs tileTex + heatManager; particle flicker is
|
||||
// computed inline using the falloutBloom particle settings) ---
|
||||
this.falloutLightPass = new FalloutLightPass(
|
||||
gl,
|
||||
mapW,
|
||||
mapH,
|
||||
this.res.tileTex,
|
||||
this.res.borderTex,
|
||||
this.heatManager,
|
||||
this.settings,
|
||||
);
|
||||
@@ -1070,8 +1070,7 @@ export class GPURenderer {
|
||||
}
|
||||
|
||||
private computeTextures(): void {
|
||||
if (this.settings.passEnabled.mapOverlay)
|
||||
this.borderPass.draw(this.frameTick);
|
||||
if (this.settings.passEnabled.mapOverlay) this.borderPass.draw();
|
||||
}
|
||||
|
||||
private renderFrame(): void {
|
||||
@@ -1086,7 +1085,7 @@ export class GPURenderer {
|
||||
const sceneTex = toTarget(this.gl, this.sceneTarget, () =>
|
||||
this.drawBaseLayer(cam),
|
||||
);
|
||||
const lightTex = this.lightmapPass.draw(cam, cw, ch);
|
||||
const lightTex = this.lightmapPass.draw(cam, cw, ch, this.frameTick);
|
||||
toScreen(this.gl, cw, ch, () =>
|
||||
this.nightCompositePass.draw(sceneTex, lightTex),
|
||||
);
|
||||
|
||||
@@ -46,6 +46,48 @@ export function buildTree(s: RenderSettings, d: RenderSettings): DebugNode[] {
|
||||
),
|
||||
slider(s.falloutBloom, "bloomCoverage", d.falloutBloom, 0, 10, 0.1),
|
||||
slider(s.falloutBloom, "heatDecayPerTick", d.falloutBloom, 0, 5, 0.01),
|
||||
color(
|
||||
s.falloutBloom,
|
||||
"particleColorDarkR",
|
||||
"particleColorDarkG",
|
||||
"particleColorDarkB",
|
||||
d.falloutBloom,
|
||||
"Particle Color Dark",
|
||||
),
|
||||
color(
|
||||
s.falloutBloom,
|
||||
"particleColorBrightR",
|
||||
"particleColorBrightG",
|
||||
"particleColorBrightB",
|
||||
d.falloutBloom,
|
||||
"Particle Color Bright",
|
||||
),
|
||||
slider(
|
||||
s.falloutBloom,
|
||||
"particleThresholdUnowned",
|
||||
d.falloutBloom,
|
||||
0.5,
|
||||
1,
|
||||
0.005,
|
||||
),
|
||||
slider(
|
||||
s.falloutBloom,
|
||||
"particleThresholdOwned",
|
||||
d.falloutBloom,
|
||||
0.5,
|
||||
1,
|
||||
0.005,
|
||||
),
|
||||
slider(
|
||||
s.falloutBloom,
|
||||
"particleFlickerSpeed",
|
||||
d.falloutBloom,
|
||||
0,
|
||||
2,
|
||||
0.01,
|
||||
),
|
||||
slider(s.falloutBloom, "particleStrength", d.falloutBloom, 0, 5, 0.01),
|
||||
slider(s.falloutBloom, "particleFreshScale", d.falloutBloom, 0, 1, 0.01),
|
||||
]),
|
||||
|
||||
folder("Day / Night", [
|
||||
@@ -79,36 +121,17 @@ export function buildTree(s: RenderSettings, d: RenderSettings): DebugNode[] {
|
||||
folder("Map Overlay", [
|
||||
slider(s.mapOverlay, "trailAlpha", d.mapOverlay, 0, 1, 0.01),
|
||||
slider(s.mapOverlay, "defenseCheckerDarken", d.mapOverlay, 0, 1, 0.01),
|
||||
slider(s.mapOverlay, "charcoalBase", d.mapOverlay, 0, 0.3, 0.005),
|
||||
slider(s.mapOverlay, "charcoalVariation", d.mapOverlay, 0, 0.3, 0.005),
|
||||
slider(s.mapOverlay, "charcoalAlpha", d.mapOverlay, 0, 1, 0.01),
|
||||
slider(
|
||||
s.mapOverlay,
|
||||
"emberThresholdUnowned",
|
||||
d.mapOverlay,
|
||||
0.5,
|
||||
1,
|
||||
0.005,
|
||||
),
|
||||
slider(s.mapOverlay, "emberThresholdOwned", d.mapOverlay, 0.5, 1, 0.005),
|
||||
slider(s.mapOverlay, "emberFlickerSpeed", d.mapOverlay, 0, 2, 0.01),
|
||||
slider(s.mapOverlay, "staleNukeBase", d.mapOverlay, 0, 0.3, 0.005),
|
||||
slider(s.mapOverlay, "staleNukeVariation", d.mapOverlay, 0, 0.3, 0.005),
|
||||
slider(s.mapOverlay, "staleNukeAlpha", d.mapOverlay, 0, 1, 0.01),
|
||||
color(
|
||||
s.mapOverlay,
|
||||
"emberColorDarkR",
|
||||
"emberColorDarkG",
|
||||
"emberColorDarkB",
|
||||
"staleNukeR",
|
||||
"staleNukeG",
|
||||
"staleNukeB",
|
||||
d.mapOverlay,
|
||||
"Ember Color Dark",
|
||||
"Stale Nuke Color",
|
||||
),
|
||||
color(
|
||||
s.mapOverlay,
|
||||
"emberColorBrightR",
|
||||
"emberColorBrightG",
|
||||
"emberColorBrightB",
|
||||
d.mapOverlay,
|
||||
"Ember Color Bright",
|
||||
),
|
||||
slider(s.mapOverlay, "emberStrengthUnowned", d.mapOverlay, 0, 2, 0.01),
|
||||
slider(
|
||||
s.mapOverlay,
|
||||
"highlightBrighten",
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
* Runs a fullscreen quad at tile resolution (mapW × mapH) and writes to an
|
||||
* RGBA8 texture:
|
||||
* R = border type: 0 = interior, 0.5 = normal border, 1.0 = highlight border
|
||||
* G = ember intensity: 0–255 (pre-computed flicker value, 0 = no ember)
|
||||
* G = unused (was ember intensity — moved to FalloutBloomPass/FalloutLightPass)
|
||||
* B = defense proximity: 1.0 if border tile is within range of same-owner defense post
|
||||
*
|
||||
* Both MapOverlayPass (daytime) and the night stamp overlay read this buffer
|
||||
@@ -48,10 +48,6 @@ export class BorderComputePass {
|
||||
private uMapSize: WebGLUniformLocation;
|
||||
private uHighlightOwner: WebGLUniformLocation;
|
||||
private uHighlightThicken: WebGLUniformLocation;
|
||||
private uTick: WebGLUniformLocation;
|
||||
private uEmberThresholdUnowned: WebGLUniformLocation;
|
||||
private uEmberThresholdOwned: WebGLUniformLocation;
|
||||
private uEmberFlickerSpeed: WebGLUniformLocation;
|
||||
private uDefensePosts: WebGLUniformLocation;
|
||||
private uDefensePostCount: WebGLUniformLocation;
|
||||
private uDefensePostRange: WebGLUniformLocation;
|
||||
@@ -91,19 +87,6 @@ export class BorderComputePass {
|
||||
this.program,
|
||||
"uHighlightThicken",
|
||||
)!;
|
||||
this.uTick = gl.getUniformLocation(this.program, "uTick")!;
|
||||
this.uEmberThresholdUnowned = gl.getUniformLocation(
|
||||
this.program,
|
||||
"uEmberThresholdUnowned",
|
||||
)!;
|
||||
this.uEmberThresholdOwned = gl.getUniformLocation(
|
||||
this.program,
|
||||
"uEmberThresholdOwned",
|
||||
)!;
|
||||
this.uEmberFlickerSpeed = gl.getUniformLocation(
|
||||
this.program,
|
||||
"uEmberFlickerSpeed",
|
||||
)!;
|
||||
this.uDefensePosts = gl.getUniformLocation(this.program, "uDefensePosts")!;
|
||||
this.uDefensePostCount = gl.getUniformLocation(
|
||||
this.program,
|
||||
@@ -131,7 +114,7 @@ export class BorderComputePass {
|
||||
});
|
||||
|
||||
// --- RGBA8 border buffer at tile resolution ---
|
||||
// R = border type, G = ember intensity, B = defense proximity flag
|
||||
// R = border type, G = unused, B = defense proximity flag
|
||||
this.borderTex = createTexture2D(gl, {
|
||||
width: mapW,
|
||||
height: mapH,
|
||||
@@ -223,7 +206,7 @@ export class BorderComputePass {
|
||||
* Compute border flags for the current frame. Call before MapOverlayPass and stamp overlay.
|
||||
* Leaves the GL state with its own FBO bound — caller must restore FBO and viewport.
|
||||
*/
|
||||
draw(tick: number): void {
|
||||
draw(): void {
|
||||
if (!this.dirty) return;
|
||||
this.dirty = false;
|
||||
|
||||
@@ -238,10 +221,6 @@ export class BorderComputePass {
|
||||
gl.uniform2f(this.uMapSize, this.mapW, this.mapH);
|
||||
gl.uniform1ui(this.uHighlightOwner, this.highlightOwner);
|
||||
gl.uniform1i(this.uHighlightThicken, Math.floor(mo.highlightThicken));
|
||||
gl.uniform1f(this.uTick, tick);
|
||||
gl.uniform1f(this.uEmberThresholdUnowned, mo.emberThresholdUnowned);
|
||||
gl.uniform1f(this.uEmberThresholdOwned, mo.emberThresholdOwned);
|
||||
gl.uniform1f(this.uEmberFlickerSpeed, mo.emberFlickerSpeed);
|
||||
gl.uniform4fv(this.uDefensePosts, this.defensePostData);
|
||||
gl.uniform1i(this.uDefensePostCount, this.defensePostCount);
|
||||
gl.uniform1f(this.uDefensePostRange, mo.defensePostRange);
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
/**
|
||||
* BorderStampPass — territory borders + defense checkerboard + embers.
|
||||
* BorderStampPass — territory borders + defense checkerboard.
|
||||
*
|
||||
* Always draws at full brightness (after the optional night composite).
|
||||
* Reads pre-computed border flags, ember intensity, and defense proximity
|
||||
* Reads pre-computed border flags and defense proximity
|
||||
* from the BorderComputePass RGBA8 buffer.
|
||||
*/
|
||||
|
||||
@@ -27,9 +27,6 @@ export class BorderStampPass {
|
||||
private uDefenseCheckerDarken: WebGLUniformLocation;
|
||||
private uEmbargoTintRatio: WebGLUniformLocation;
|
||||
private uFriendlyTintRatio: WebGLUniformLocation;
|
||||
private uEmberColorDark: WebGLUniformLocation;
|
||||
private uEmberColorBright: WebGLUniformLocation;
|
||||
private uEmberStrengthUnowned: WebGLUniformLocation;
|
||||
private uAltView: WebGLUniformLocation;
|
||||
|
||||
private vao: WebGLVertexArrayObject;
|
||||
@@ -82,18 +79,6 @@ export class BorderStampPass {
|
||||
this.program,
|
||||
"uFriendlyTintRatio",
|
||||
)!;
|
||||
this.uEmberColorDark = gl.getUniformLocation(
|
||||
this.program,
|
||||
"uEmberColorDark",
|
||||
)!;
|
||||
this.uEmberColorBright = gl.getUniformLocation(
|
||||
this.program,
|
||||
"uEmberColorBright",
|
||||
)!;
|
||||
this.uEmberStrengthUnowned = gl.getUniformLocation(
|
||||
this.program,
|
||||
"uEmberStrengthUnowned",
|
||||
)!;
|
||||
this.uAltView = gl.getUniformLocation(this.program, "uAltView")!;
|
||||
|
||||
gl.useProgram(this.program);
|
||||
@@ -112,7 +97,7 @@ export class BorderStampPass {
|
||||
this.affiliationTex = tex;
|
||||
}
|
||||
|
||||
/** Draw borders + defense checkerboard + embers. Blending must be enabled. */
|
||||
/** Draw borders + defense checkerboard. Blending must be enabled. */
|
||||
draw(cameraMatrix: Float32Array): void {
|
||||
const gl = this.gl;
|
||||
const mo = this.settings.mapOverlay;
|
||||
@@ -124,19 +109,6 @@ export class BorderStampPass {
|
||||
gl.uniform1f(this.uDefenseCheckerDarken, mo.defenseCheckerDarken);
|
||||
gl.uniform1f(this.uEmbargoTintRatio, mo.embargoTintRatio);
|
||||
gl.uniform1f(this.uFriendlyTintRatio, mo.friendlyTintRatio);
|
||||
gl.uniform3f(
|
||||
this.uEmberColorDark,
|
||||
mo.emberColorDarkR,
|
||||
mo.emberColorDarkG,
|
||||
mo.emberColorDarkB,
|
||||
);
|
||||
gl.uniform3f(
|
||||
this.uEmberColorBright,
|
||||
mo.emberColorBrightR,
|
||||
mo.emberColorBrightG,
|
||||
mo.emberColorBrightB,
|
||||
);
|
||||
gl.uniform1f(this.uEmberStrengthUnowned, mo.emberStrengthUnowned);
|
||||
gl.uniform1i(this.uAltView, this.altView ? 1 : 0);
|
||||
|
||||
gl.activeTexture(gl.TEXTURE0);
|
||||
|
||||
@@ -57,6 +57,13 @@ export class FalloutBloomPass {
|
||||
private uMetaInfluenceHot: WebGLUniformLocation;
|
||||
private uOpacityFadeEnd: WebGLUniformLocation;
|
||||
private uBloomColor: WebGLUniformLocation;
|
||||
private uParticleColorDark: WebGLUniformLocation;
|
||||
private uParticleColorBright: WebGLUniformLocation;
|
||||
private uParticleThresholdUnowned: WebGLUniformLocation;
|
||||
private uParticleThresholdOwned: WebGLUniformLocation;
|
||||
private uParticleFlickerSpeed: WebGLUniformLocation;
|
||||
private uParticleStrength: WebGLUniformLocation;
|
||||
private uParticleFreshScale: WebGLUniformLocation;
|
||||
|
||||
// Uniforms — composite
|
||||
private uCompositeCam: WebGLUniformLocation;
|
||||
@@ -147,6 +154,34 @@ export class FalloutBloomPass {
|
||||
"uOpacityFadeEnd",
|
||||
)!;
|
||||
this.uBloomColor = gl.getUniformLocation(this.extractProg, "uBloomColor")!;
|
||||
this.uParticleColorDark = gl.getUniformLocation(
|
||||
this.extractProg,
|
||||
"uParticleColorDark",
|
||||
)!;
|
||||
this.uParticleColorBright = gl.getUniformLocation(
|
||||
this.extractProg,
|
||||
"uParticleColorBright",
|
||||
)!;
|
||||
this.uParticleThresholdUnowned = gl.getUniformLocation(
|
||||
this.extractProg,
|
||||
"uParticleThresholdUnowned",
|
||||
)!;
|
||||
this.uParticleThresholdOwned = gl.getUniformLocation(
|
||||
this.extractProg,
|
||||
"uParticleThresholdOwned",
|
||||
)!;
|
||||
this.uParticleFlickerSpeed = gl.getUniformLocation(
|
||||
this.extractProg,
|
||||
"uParticleFlickerSpeed",
|
||||
)!;
|
||||
this.uParticleStrength = gl.getUniformLocation(
|
||||
this.extractProg,
|
||||
"uParticleStrength",
|
||||
)!;
|
||||
this.uParticleFreshScale = gl.getUniformLocation(
|
||||
this.extractProg,
|
||||
"uParticleFreshScale",
|
||||
)!;
|
||||
gl.useProgram(this.extractProg);
|
||||
gl.uniform1i(gl.getUniformLocation(this.extractProg, "uTileTex"), 0);
|
||||
gl.uniform1i(gl.getUniformLocation(this.extractProg, "uHeatTex"), 1);
|
||||
@@ -257,6 +292,23 @@ export class FalloutBloomPass {
|
||||
gl.uniform1f(this.uMetaInfluenceHot, fb.metaInfluenceHot);
|
||||
gl.uniform1f(this.uOpacityFadeEnd, fb.opacityFadeEnd);
|
||||
gl.uniform3f(this.uBloomColor, fb.bloomR, fb.bloomG, fb.bloomB);
|
||||
gl.uniform3f(
|
||||
this.uParticleColorDark,
|
||||
fb.particleColorDarkR,
|
||||
fb.particleColorDarkG,
|
||||
fb.particleColorDarkB,
|
||||
);
|
||||
gl.uniform3f(
|
||||
this.uParticleColorBright,
|
||||
fb.particleColorBrightR,
|
||||
fb.particleColorBrightG,
|
||||
fb.particleColorBrightB,
|
||||
);
|
||||
gl.uniform1f(this.uParticleThresholdUnowned, fb.particleThresholdUnowned);
|
||||
gl.uniform1f(this.uParticleThresholdOwned, fb.particleThresholdOwned);
|
||||
gl.uniform1f(this.uParticleFlickerSpeed, fb.particleFlickerSpeed);
|
||||
gl.uniform1f(this.uParticleStrength, fb.particleStrength);
|
||||
gl.uniform1f(this.uParticleFreshScale, fb.particleFreshScale);
|
||||
|
||||
gl.activeTexture(gl.TEXTURE0);
|
||||
gl.bindTexture(gl.TEXTURE_2D, this.tileTex);
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
* FalloutLightPass — tile-space fallout light extraction + composite.
|
||||
*
|
||||
* Extracted from LightmapPass. Two-step:
|
||||
* 1. Extract fallout light at tile resolution (mapW x mapH) — reads heat + embers
|
||||
* 1. Extract fallout light at tile resolution (mapW x mapH) — reads heat and
|
||||
* computes the same particle flicker as FalloutBloomPass inline
|
||||
* 2. Composite into the target lightmap FBO via camera-projected map quad (additive)
|
||||
*/
|
||||
|
||||
@@ -28,7 +29,6 @@ export class FalloutLightPass {
|
||||
private mapH: number;
|
||||
private heatManager: HeatManager;
|
||||
private tileTex: WebGLTexture;
|
||||
private borderTex: WebGLTexture;
|
||||
|
||||
// Fallout light extraction
|
||||
private falloutLightProg: WebGLProgram;
|
||||
@@ -38,6 +38,11 @@ export class FalloutLightPass {
|
||||
private uFalloutLightThreshold: WebGLUniformLocation;
|
||||
private uEmberLightColor: WebGLUniformLocation;
|
||||
private uEmberLightIntensity: WebGLUniformLocation;
|
||||
private uFalloutTick: WebGLUniformLocation;
|
||||
private uParticleThresholdUnowned: WebGLUniformLocation;
|
||||
private uParticleThresholdOwned: WebGLUniformLocation;
|
||||
private uParticleFlickerSpeed: WebGLUniformLocation;
|
||||
private uParticleFreshScale: WebGLUniformLocation;
|
||||
|
||||
// Fallout composite (tile-space → lightmap)
|
||||
private falloutCompositeProg: WebGLProgram;
|
||||
@@ -57,7 +62,6 @@ export class FalloutLightPass {
|
||||
mapW: number,
|
||||
mapH: number,
|
||||
tileTex: WebGLTexture,
|
||||
borderTex: WebGLTexture,
|
||||
heatManager: HeatManager,
|
||||
settings: RenderSettings,
|
||||
) {
|
||||
@@ -66,7 +70,6 @@ export class FalloutLightPass {
|
||||
this.mapW = mapW;
|
||||
this.mapH = mapH;
|
||||
this.tileTex = tileTex;
|
||||
this.borderTex = borderTex;
|
||||
this.heatManager = heatManager;
|
||||
|
||||
// Fallout light extraction program
|
||||
@@ -99,10 +102,26 @@ export class FalloutLightPass {
|
||||
this.falloutLightProg,
|
||||
"uEmberLightIntensity",
|
||||
)!;
|
||||
this.uFalloutTick = gl.getUniformLocation(this.falloutLightProg, "uTick")!;
|
||||
this.uParticleThresholdUnowned = gl.getUniformLocation(
|
||||
this.falloutLightProg,
|
||||
"uParticleThresholdUnowned",
|
||||
)!;
|
||||
this.uParticleThresholdOwned = gl.getUniformLocation(
|
||||
this.falloutLightProg,
|
||||
"uParticleThresholdOwned",
|
||||
)!;
|
||||
this.uParticleFlickerSpeed = gl.getUniformLocation(
|
||||
this.falloutLightProg,
|
||||
"uParticleFlickerSpeed",
|
||||
)!;
|
||||
this.uParticleFreshScale = gl.getUniformLocation(
|
||||
this.falloutLightProg,
|
||||
"uParticleFreshScale",
|
||||
)!;
|
||||
gl.useProgram(this.falloutLightProg);
|
||||
gl.uniform1i(gl.getUniformLocation(this.falloutLightProg, "uHeatTex"), 0);
|
||||
gl.uniform1i(gl.getUniformLocation(this.falloutLightProg, "uTileTex"), 1);
|
||||
gl.uniform1i(gl.getUniformLocation(this.falloutLightProg, "uBorderTex"), 2);
|
||||
|
||||
// Fallout composite program
|
||||
this.falloutCompositeProg = createProgram(
|
||||
@@ -170,9 +189,11 @@ export class FalloutLightPass {
|
||||
targetFbo: WebGLFramebuffer,
|
||||
targetW: number,
|
||||
targetH: number,
|
||||
tick: number,
|
||||
): void {
|
||||
const gl = this.gl;
|
||||
const dn = this.settings.dayNight;
|
||||
const fb = this.settings.falloutBloom;
|
||||
|
||||
// Step 1: Extract fallout light in tile space
|
||||
gl.bindFramebuffer(gl.FRAMEBUFFER, this.falloutFbo);
|
||||
@@ -198,13 +219,16 @@ export class FalloutLightPass {
|
||||
dn.emberLightB,
|
||||
);
|
||||
gl.uniform1f(this.uEmberLightIntensity, dn.emberLightIntensity);
|
||||
gl.uniform1f(this.uFalloutTick, tick);
|
||||
gl.uniform1f(this.uParticleThresholdUnowned, fb.particleThresholdUnowned);
|
||||
gl.uniform1f(this.uParticleThresholdOwned, fb.particleThresholdOwned);
|
||||
gl.uniform1f(this.uParticleFlickerSpeed, fb.particleFlickerSpeed);
|
||||
gl.uniform1f(this.uParticleFreshScale, fb.particleFreshScale);
|
||||
|
||||
gl.activeTexture(gl.TEXTURE0);
|
||||
gl.bindTexture(gl.TEXTURE_2D, this.heatManager.getHeatTex());
|
||||
gl.activeTexture(gl.TEXTURE1);
|
||||
gl.bindTexture(gl.TEXTURE_2D, this.tileTex);
|
||||
gl.activeTexture(gl.TEXTURE2);
|
||||
gl.bindTexture(gl.TEXTURE_2D, this.borderTex);
|
||||
gl.bindVertexArray(this.quadVao);
|
||||
gl.drawArrays(gl.TRIANGLES, 0, 6);
|
||||
|
||||
|
||||
@@ -142,6 +142,7 @@ export class LightmapPass {
|
||||
cameraMatrix: Float32Array,
|
||||
sceneW: number,
|
||||
sceneH: number,
|
||||
tick: number,
|
||||
): WebGLTexture {
|
||||
const gl = this.gl;
|
||||
const lw = Math.max(1, sceneW >> 1);
|
||||
@@ -159,7 +160,7 @@ export class LightmapPass {
|
||||
this.pointLightPass.draw(cameraMatrix);
|
||||
|
||||
// --- 2. Fallout light → extract at tile res, composite into FBO A (additive) ---
|
||||
this.falloutLightPass.draw(cameraMatrix, this.lightFboA, lw, lh);
|
||||
this.falloutLightPass.draw(cameraMatrix, this.lightFboA, lw, lh, tick);
|
||||
|
||||
// --- 3. Blur: 2 iterations separable H+V Gaussian ---
|
||||
const zoom = Math.abs(cameraMatrix[0]);
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
/**
|
||||
* TerritoryPass — territory fill + fallout charcoal ground.
|
||||
* TerritoryPass — territory fill + stale-nuke ground.
|
||||
*
|
||||
* Draws only what should be darkened by the night cycle:
|
||||
* - Owned territory (player color fill)
|
||||
* - Unowned fallout (charcoal ground)
|
||||
* - Any fallout tile (stale-nuke ground, overrides owned territory)
|
||||
*
|
||||
* No borders, embers, trails, or defense checkerboard — those are
|
||||
* handled by BorderStampPass and TrailPass at full brightness.
|
||||
@@ -31,9 +31,10 @@ export class TerritoryPass {
|
||||
private uCamera: WebGLUniformLocation;
|
||||
private uMapSize: WebGLUniformLocation;
|
||||
private uAltView: WebGLUniformLocation;
|
||||
private uCharcoalBase: WebGLUniformLocation;
|
||||
private uCharcoalVariation: WebGLUniformLocation;
|
||||
private uCharcoalAlpha: WebGLUniformLocation;
|
||||
private uStaleNukeBase: WebGLUniformLocation;
|
||||
private uStaleNukeVariation: WebGLUniformLocation;
|
||||
private uStaleNukeAlpha: WebGLUniformLocation;
|
||||
private uStaleNukeColor: WebGLUniformLocation;
|
||||
private uHighlightOwner: WebGLUniformLocation;
|
||||
private uHighlightBrighten: WebGLUniformLocation;
|
||||
private uShowPatterns: WebGLUniformLocation;
|
||||
@@ -103,14 +104,21 @@ export class TerritoryPass {
|
||||
this.uCamera = gl.getUniformLocation(this.program, "uCamera")!;
|
||||
this.uMapSize = gl.getUniformLocation(this.program, "uMapSize")!;
|
||||
this.uAltView = gl.getUniformLocation(this.program, "uAltView")!;
|
||||
this.uCharcoalBase = gl.getUniformLocation(this.program, "uCharcoalBase")!;
|
||||
this.uCharcoalVariation = gl.getUniformLocation(
|
||||
this.uStaleNukeBase = gl.getUniformLocation(
|
||||
this.program,
|
||||
"uCharcoalVariation",
|
||||
"uStaleNukeBase",
|
||||
)!;
|
||||
this.uCharcoalAlpha = gl.getUniformLocation(
|
||||
this.uStaleNukeVariation = gl.getUniformLocation(
|
||||
this.program,
|
||||
"uCharcoalAlpha",
|
||||
"uStaleNukeVariation",
|
||||
)!;
|
||||
this.uStaleNukeAlpha = gl.getUniformLocation(
|
||||
this.program,
|
||||
"uStaleNukeAlpha",
|
||||
)!;
|
||||
this.uStaleNukeColor = gl.getUniformLocation(
|
||||
this.program,
|
||||
"uStaleNukeColor",
|
||||
)!;
|
||||
this.uHighlightOwner = gl.getUniformLocation(
|
||||
this.program,
|
||||
@@ -362,7 +370,7 @@ export class TerritoryPass {
|
||||
this.highlightOwner = ownerID;
|
||||
}
|
||||
|
||||
/** Draw territory fill + fallout charcoal. Blending must be enabled by caller. */
|
||||
/** Draw territory fill + stale-nuke ground. Blending must be enabled by caller. */
|
||||
draw(cameraMatrix: Float32Array): void {
|
||||
this.flushTileTexture();
|
||||
|
||||
@@ -373,9 +381,15 @@ export class TerritoryPass {
|
||||
gl.uniformMatrix3fv(this.uCamera, false, cameraMatrix);
|
||||
gl.uniform2f(this.uMapSize, this.mapW, this.mapH);
|
||||
gl.uniform1i(this.uAltView, this.altView ? 1 : 0);
|
||||
gl.uniform1f(this.uCharcoalBase, mo.charcoalBase);
|
||||
gl.uniform1f(this.uCharcoalVariation, mo.charcoalVariation);
|
||||
gl.uniform1f(this.uCharcoalAlpha, mo.charcoalAlpha);
|
||||
gl.uniform1f(this.uStaleNukeBase, mo.staleNukeBase);
|
||||
gl.uniform1f(this.uStaleNukeVariation, mo.staleNukeVariation);
|
||||
gl.uniform1f(this.uStaleNukeAlpha, mo.staleNukeAlpha);
|
||||
gl.uniform3f(
|
||||
this.uStaleNukeColor,
|
||||
mo.staleNukeR,
|
||||
mo.staleNukeG,
|
||||
mo.staleNukeB,
|
||||
);
|
||||
gl.uniform1ui(this.uHighlightOwner, this.highlightOwner);
|
||||
gl.uniform1f(this.uHighlightBrighten, mo.highlightFillBrighten);
|
||||
gl.uniform1i(
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
"contrastHiHot": 0,
|
||||
"metaFreq": 0.02,
|
||||
"intensityCold": 0.15,
|
||||
"intensityHot": 0.6,
|
||||
"intensityHot": 1.8,
|
||||
"metaInfluenceCold": 1,
|
||||
"metaInfluenceHot": 0,
|
||||
"opacityFadeEnd": 1,
|
||||
@@ -31,7 +31,18 @@
|
||||
"bloomG": 0.8196078431372549,
|
||||
"bloomB": 0,
|
||||
"bloomCoverage": 1.1,
|
||||
"heatDecayPerTick": 1
|
||||
"heatDecayPerTick": 1,
|
||||
"particleColorDarkR": 0.05,
|
||||
"particleColorDarkG": 0.4,
|
||||
"particleColorDarkB": 0.05,
|
||||
"particleColorBrightR": 0.2,
|
||||
"particleColorBrightG": 1,
|
||||
"particleColorBrightB": 0.2,
|
||||
"particleThresholdUnowned": 0.85,
|
||||
"particleThresholdOwned": 0.875,
|
||||
"particleFlickerSpeed": 0.2,
|
||||
"particleStrength": 1,
|
||||
"particleFreshScale": 0.2
|
||||
},
|
||||
"dayNight": {
|
||||
"mode": "light",
|
||||
@@ -53,19 +64,12 @@
|
||||
"mapOverlay": {
|
||||
"trailAlpha": 0.588,
|
||||
"defenseCheckerDarken": 0.7,
|
||||
"charcoalBase": 0,
|
||||
"charcoalVariation": 0.05,
|
||||
"charcoalAlpha": 0.87,
|
||||
"emberThresholdUnowned": 0.85,
|
||||
"emberThresholdOwned": 0.875,
|
||||
"emberFlickerSpeed": 0.12,
|
||||
"emberColorDarkR": 0.6,
|
||||
"emberColorDarkG": 0.15,
|
||||
"emberColorDarkB": 0,
|
||||
"emberColorBrightR": 1,
|
||||
"emberColorBrightG": 0.5,
|
||||
"emberColorBrightB": 0.05,
|
||||
"emberStrengthUnowned": 0.5,
|
||||
"staleNukeBase": 0,
|
||||
"staleNukeVariation": 0.05,
|
||||
"staleNukeAlpha": 1,
|
||||
"staleNukeR": 0.05,
|
||||
"staleNukeG": 0.55,
|
||||
"staleNukeB": 0.07,
|
||||
"highlightBrighten": 0.25,
|
||||
"highlightFillBrighten": 0.15,
|
||||
"highlightThicken": 2,
|
||||
|
||||
@@ -7,10 +7,6 @@ uniform usampler2D uRelationTex; // R8UI — relationship matrix (ownerA × owne
|
||||
uniform vec2 uMapSize;
|
||||
uniform uint uHighlightOwner;
|
||||
uniform int uHighlightThicken; // Chebyshev radius for highlight expansion
|
||||
uniform float uTick;
|
||||
uniform float uEmberThresholdUnowned;
|
||||
uniform float uEmberThresholdOwned;
|
||||
uniform float uEmberFlickerSpeed;
|
||||
|
||||
// Defense post proximity — (x, y, ownerID, _) per post
|
||||
uniform vec4 uDefensePosts[MAX_DEFENSE_POSTS];
|
||||
@@ -31,7 +27,6 @@ void main() {
|
||||
|
||||
uint raw = texelFetch(uTileTex, tc, 0).r;
|
||||
uint owner = raw & uint(OWNER_MASK);
|
||||
bool fallout = (raw & (1u << FALLOUT_BIT)) != 0u;
|
||||
|
||||
// --- Border detection ---
|
||||
float borderType = 0.0; // 0=interior, ~0.5=normal border, ~1.0=highlight border
|
||||
@@ -104,20 +99,9 @@ void main() {
|
||||
}
|
||||
}
|
||||
|
||||
// --- Ember detection ---
|
||||
float emberIntensity = 0.0;
|
||||
if (fallout) {
|
||||
float h = fract(sin(float(tc.x) * 12.9898 + float(tc.y) * 78.233) * 43758.5453);
|
||||
float h2 = fract(sin(float(tc.x) * 63.7 + float(tc.y) * 157.3) * 23421.631);
|
||||
float threshold = (owner == 0u) ? uEmberThresholdUnowned : uEmberThresholdOwned;
|
||||
if (h2 > threshold) {
|
||||
float flicker = max(0.0, sin(uTick * uEmberFlickerSpeed + h * 12.0) * 0.8 + 0.2);
|
||||
flicker *= flicker; // sharpen
|
||||
emberIntensity = flicker;
|
||||
}
|
||||
}
|
||||
|
||||
// A = relationship: 0.0=neutral, 0.5=friendly, 1.0=embargo
|
||||
float relation = float(maxRel) * 0.5;
|
||||
fragColor = vec4(borderType, emberIntensity, defenseFlag, relation);
|
||||
// G channel is unused (formerly emberIntensity; ember is now computed in
|
||||
// FalloutBloomPass and FalloutLightPass).
|
||||
fragColor = vec4(borderType, 0.0, defenseFlag, relation);
|
||||
}
|
||||
|
||||
@@ -12,9 +12,6 @@ uniform float uHighlightBrighten;
|
||||
uniform float uDefenseCheckerDarken;
|
||||
uniform float uEmbargoTintRatio;
|
||||
uniform float uFriendlyTintRatio;
|
||||
uniform vec3 uEmberColorDark;
|
||||
uniform vec3 uEmberColorBright;
|
||||
uniform float uEmberStrengthUnowned;
|
||||
|
||||
in vec2 vWorldPos;
|
||||
out vec4 fragColor;
|
||||
@@ -29,7 +26,6 @@ void main() {
|
||||
// Read pre-computed border flags from BorderComputePass
|
||||
vec4 borderData = texelFetch(uBorderTex, tc, 0);
|
||||
float borderType = borderData.r; // 0=interior, ~0.5=normal, ~1.0=highlight
|
||||
float emberIntensity = borderData.g; // 0–1 flicker value
|
||||
bool defense = borderData.b > 0.5; // defense post proximity
|
||||
float relation = borderData.a; // 0.0=neutral, ~0.5=friendly, ~1.0=embargo
|
||||
|
||||
@@ -64,16 +60,5 @@ void main() {
|
||||
return;
|
||||
}
|
||||
|
||||
// --- Ember stamp: full-brightness ember on fallout tiles ---
|
||||
if (emberIntensity > 0.0) {
|
||||
float h = fract(sin(float(tc.x) * 12.9898 + float(tc.y) * 78.233) * 43758.5453);
|
||||
vec3 ember = mix(uEmberColorDark, uEmberColorBright, h) * emberIntensity * uEmberStrengthUnowned;
|
||||
float a = max(ember.r, max(ember.g, ember.b));
|
||||
if (a > 0.01) {
|
||||
fragColor = vec4(ember, 1.0);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
discard;
|
||||
}
|
||||
|
||||
@@ -3,13 +3,17 @@ precision highp float;
|
||||
precision highp usampler2D;
|
||||
uniform sampler2D uHeatTex;
|
||||
uniform usampler2D uTileTex;
|
||||
uniform sampler2D uBorderTex;
|
||||
uniform vec2 uMapSize;
|
||||
uniform float uTick;
|
||||
uniform vec3 uFalloutLightColor;
|
||||
uniform float uFalloutLightIntensity;
|
||||
uniform float uFalloutLightThreshold;
|
||||
uniform vec3 uEmberLightColor;
|
||||
uniform float uEmberLightIntensity;
|
||||
uniform float uParticleThresholdUnowned;
|
||||
uniform float uParticleThresholdOwned;
|
||||
uniform float uParticleFlickerSpeed;
|
||||
uniform float uParticleFreshScale;
|
||||
out vec4 fragColor;
|
||||
void main() {
|
||||
ivec2 tc = ivec2(gl_FragCoord.xy);
|
||||
@@ -19,6 +23,7 @@ void main() {
|
||||
bool fallout = (raw & (1u << FALLOUT_BIT)) != 0u;
|
||||
if (!fallout) discard;
|
||||
|
||||
uint owner = raw & uint(OWNER_MASK);
|
||||
float heat = texelFetch(uHeatTex, tc, 0).r;
|
||||
|
||||
// Green fallout glow
|
||||
@@ -28,10 +33,16 @@ void main() {
|
||||
light += uFalloutLightColor * fi;
|
||||
}
|
||||
|
||||
// Ember light — read pre-computed flicker from BorderComputePass
|
||||
float emberIntensity = texelFetch(uBorderTex, tc, 0).g;
|
||||
if (emberIntensity > 0.0) {
|
||||
light += uEmberLightColor * emberIntensity * uEmberLightIntensity;
|
||||
// Ember light — compute the same flicker as FalloutBloomPass.extract inline.
|
||||
float h1 = fract(sin(float(tc.x) * 12.9898 + float(tc.y) * 78.233) * 43758.5453);
|
||||
float h2 = fract(sin(float(tc.x) * 63.7 + float(tc.y) * 157.3) * 23421.631);
|
||||
float pThresh = (owner == 0u) ? uParticleThresholdUnowned : uParticleThresholdOwned;
|
||||
if (h2 > pThresh) {
|
||||
float tileRate = uParticleFlickerSpeed * (0.4 + h1 * 1.2);
|
||||
float flick = max(0.0, sin(uTick * tileRate + h1 * 12.0) * 0.8 + 0.2);
|
||||
flick *= flick;
|
||||
flick *= mix(uParticleFreshScale, 1.0, 1.0 - heat);
|
||||
light += uEmberLightColor * flick * uEmberLightIntensity;
|
||||
}
|
||||
|
||||
float a = max(light.r, max(light.g, light.b));
|
||||
|
||||
@@ -20,6 +20,13 @@ uniform float uMetaInfluenceCold;
|
||||
uniform float uMetaInfluenceHot;
|
||||
uniform float uOpacityFadeEnd;
|
||||
uniform vec3 uBloomColor;
|
||||
uniform vec3 uParticleColorDark;
|
||||
uniform vec3 uParticleColorBright;
|
||||
uniform float uParticleThresholdUnowned;
|
||||
uniform float uParticleThresholdOwned;
|
||||
uniform float uParticleFlickerSpeed;
|
||||
uniform float uParticleStrength;
|
||||
uniform float uParticleFreshScale;
|
||||
|
||||
uniform sampler2D uHeatTex;
|
||||
|
||||
@@ -55,6 +62,7 @@ void main() {
|
||||
|
||||
uint raw = texelFetch(uTileTex, tc, 0).r;
|
||||
if ((raw & (1u << FALLOUT_BIT)) == 0u) discard;
|
||||
uint owner = raw & uint(OWNER_MASK);
|
||||
|
||||
float heat = texelFetch(uHeatTex, tc, 0).r;
|
||||
vec2 tileCenter = vec2(tc) + 0.5;
|
||||
@@ -79,4 +87,22 @@ void main() {
|
||||
float opacity = smoothstep(0.0, uOpacityFadeEnd, heat);
|
||||
|
||||
fragColor = vec4(uBloomColor, 1.0) * broil * intensity * opacity;
|
||||
|
||||
// Particle dots — sharper per-tile flicker gated by a stochastic hash.
|
||||
// (Relocated here from BorderStampPass; this is fallout-domain logic.)
|
||||
float h1 = fract(sin(float(tc.x) * 12.9898 + float(tc.y) * 78.233) * 43758.5453);
|
||||
float h2 = fract(sin(float(tc.x) * 63.7 + float(tc.y) * 157.3) * 23421.631);
|
||||
float pThresh = (owner == 0u) ? uParticleThresholdUnowned : uParticleThresholdOwned;
|
||||
if (h2 > pThresh) {
|
||||
// Per-tile rate variation breaks the global rhythm so tiles don't all
|
||||
// pulse at the same frequency. h1 spans [0,1] → rate spans 0.4×–1.6× base.
|
||||
float tileRate = uParticleFlickerSpeed * (0.4 + h1 * 1.2);
|
||||
float flick = max(0.0, sin(uTick * tileRate + h1 * 12.0) * 0.8 + 0.2);
|
||||
flick *= flick;
|
||||
// Dampen when fresh (high heat); ramp to full as heat decays.
|
||||
flick *= mix(uParticleFreshScale, 1.0, 1.0 - heat);
|
||||
vec3 pc = mix(uParticleColorDark, uParticleColorBright, h1) * flick * uParticleStrength;
|
||||
float pa = max(pc.r, max(pc.g, pc.b));
|
||||
fragColor += vec4(pc, pa);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,9 +10,10 @@ uniform int uShowPatterns;
|
||||
|
||||
uniform vec2 uMapSize;
|
||||
uniform int uAltView;
|
||||
uniform float uCharcoalBase;
|
||||
uniform float uCharcoalVariation;
|
||||
uniform float uCharcoalAlpha;
|
||||
uniform float uStaleNukeBase;
|
||||
uniform float uStaleNukeVariation;
|
||||
uniform float uStaleNukeAlpha;
|
||||
uniform vec3 uStaleNukeColor;
|
||||
uniform uint uHighlightOwner; // 0 = no highlight; otherwise smallID of hovered owner
|
||||
uniform float uHighlightBrighten; // mix amount toward white for highlighted tiles
|
||||
|
||||
@@ -30,18 +31,20 @@ void main() {
|
||||
|
||||
if (owner == 0u && !fallout) discard;
|
||||
|
||||
// Alt-view: hide territory fill, keep fallout charcoal
|
||||
if (uAltView != 0 && owner != 0u) discard;
|
||||
|
||||
// --- Fallout charcoal ground (unowned) ---
|
||||
if (owner == 0u && fallout) {
|
||||
// --- Stale-nuke ground (any fallout tile, owned or not) ---
|
||||
// Renders for owned tiles too so the player's territory color can't bleed
|
||||
// through dim/transparent spots in the fallout bloom above.
|
||||
if (fallout) {
|
||||
float h = fract(sin(float(tc.x) * 12.9898 + float(tc.y) * 78.233) * 43758.5453);
|
||||
float charcoal = uCharcoalBase + h * uCharcoalVariation;
|
||||
fragColor = vec4(vec3(charcoal), uCharcoalAlpha);
|
||||
float noise = uStaleNukeBase + h * uStaleNukeVariation;
|
||||
fragColor = vec4(uStaleNukeColor + vec3(noise), uStaleNukeAlpha);
|
||||
return;
|
||||
}
|
||||
|
||||
// --- Territory fill (owned) ---
|
||||
// Alt-view: hide owned non-fallout tiles
|
||||
if (uAltView != 0) discard;
|
||||
|
||||
// --- Territory fill (owned, not fallout) ---
|
||||
float u = (float(owner) + 0.5) / float(PALETTE_SIZE);
|
||||
vec4 color = texture(uPalette, vec2(u, 0.25));
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ export interface GPUResources {
|
||||
tileTex: WebGLTexture; // R16UI — tile ownership + flags
|
||||
trailTex: WebGLTexture; // R8UI — trail owner per tile
|
||||
paletteTex: WebGLTexture; // RGBA32F — player colors
|
||||
borderTex: WebGLTexture; // RGBA8 — border type + ember + defense
|
||||
borderTex: WebGLTexture; // RGBA8 — border type + defense + relation (G unused)
|
||||
heatTexA: WebGLTexture; // R8 — fallout heat ping-pong A
|
||||
heatTexB: WebGLTexture; // R8 — fallout heat ping-pong B
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user