overlay-rectangle-pop-mechanisms.md 3.5 KB

Research: global-basemap vs bounded local overlay "rectangular pop-in"

Ticket: 05 — 精细矩形跳变的成因与可用手段 Date: 2026-09-18 Scope: decision facts only. No production cockpit code was modified; no build/dev/serve was run (repo rule).

Bottom line

The fine-overlay rectangle edge is a GLSL hard clip with zero feathering and zero cross-fade anywhere in the engine. minimumLevel: 0 in the config is a no-op (equal to the provider default), so the fine layer is not "deferred until close". There are two distinct pops to handle, and nothing built-in to reuse — any fade must be hand-rolled.

1. Layer stack

  • Global basemap: Natural Earth II TMS (Assets/Textures/NaturalEarthII), levels 0–2 only → max ~19.5 km/texel. The county is ~6×6 basemap texels.
  • Fine overlay: /gis/pic3 TMS, provider rectangle = [109.962, 27.963, 111.046, 29.061], alpha = 1.0, minimumLevel = 0, maximumLevel = 19.
  • Composite order: [NaturalEarthII, 精细层], base color #04121f.

2. Hard edge, no feathering, no cross-fade

GlobeFS.glsl clips with step() — textureAlpha *= alphaMultiplier.x * alphaMultiplier.y — a binary 0/1 mask. No feather width, no blend, no dither. Outside its rectangle the overlay contributes nothing and the basemap shows through (the base layer stretches its edge texels globally).

Two layers blend by plain alpha-over (sourceAlpha = alpha * textureAlpha); alpha here is the whole-layer ImageryLayer.alpha uniform, not a per-tile fade. TileImagery/Imagery carry no alpha at all.

3. minimumLevel: 0 is a no-op

UrlTemplateImageryProvider._minimumLevel defaults to 0, so the config value changes nothing. The overlay is eligible from its coarsest pyramid level immediately — it is not deferred until the camera is close.

4. Two pops

  • (a) First-tile appearance: a layer with no ready imagery is skipped entirely (if (!defined(imagery) || imagery.imageryLayer.alpha === 0.0) continue;), so the rectangle switches on in one frame at full alpha.
  • (b) Terrain-LOD-driven level swaps: imagery level is slaved to terrain level (targetGeometricError = terrainProvider.getLevelMaximumGeometricError(tile.level)), so every terrain refinement also bumps the imagery level with no transition — ongoing pops inside the rectangle.

5. Available knobs and their limits

  • ImageryLayer.alpha — writable, whole-layer uniform only (can fade the whole patch, cannot feather the edge). The JSDoc function(frameState, layer, x, y, level) form is not implemented in the installed engine.
  • ImageryLayer.show — binary; hiding the basemap promotes the overlay to base layer (global texel stretch) — a trap.
  • ImageryLayer.rectangle — read-only getter; not animatable.
  • minimumTerrainLevel / maximumTerrainLevel — hard on/off gate by terrain LOD; a cheap "don't show when far" proxy, but not a fade.
  • brightness/contrast/gamma/saturation/hue — color-matching only; cannot remove the resolution step.
  • readyEvent — raised only by fromProviderAsync, but the repo builds overlays with new ImageryLayer(provider), so there is no provider-ready signal for the overlay.
  • Globe.tileLoadProgressEvent — the only built-in load-progress signal.
  • Scene.preRender / postRender — the host hook for a per-frame fade.

6. History

Tickets 21 and 30 did not attempt or defer any color matching, edge feathering, cross-fade, or LOD transition — this is a fresh work surface.