Art Direction & Lighting
Readable, toylike-but-grounded look; one dynamic sun/sky, no baked lighting.
Direction
Aim for a painterly look: warm, illustrative, hand-painted feel; clear shape and readable silhouettes over photoreal detail — toylike but grounded, warm daylight default, consistent with the idea map’s own tagging habit.
Lighting
Single dynamic sun/sky lighting the whole city, tied to the day/night cycle (Time & Weather doc). The day/night terminator is softened via smoothstep on the Lambertian dot product, and the heightmap uses linear filtering + mipmaps so central-difference normal sampling stays smooth across texel boundaries. Soft real-time shadows; no per-building baked lightmaps.
Atmosphere (Fog & Horizon)
The terrain has reached its intended km scale (256×256 cells at 20m/cell = 5.12km²), so the earlier "51m prototype map" condition for the horizon plane — a large flat camera-relative quad sharing the terrain shader, extended past the procedural terrain so fog reaches the visual horizon before its edge shows — no longer holds; it can now shrink or be removed.
The fog factor is 1 - exp(-viewDistance * density) per cm, where viewDistance is the Euclidean length of the camera-relative vertex position (so it survives the floating-origin shift; see the floating-origin section in the rendering notes for the why). The fog color tracks the sky's horizon palette via the same dayBrightness(dayPhase) curve the sky uses, blending a per-weather day/night pair. FOG_BY_WEATHER in tweaks.ts is the single source of truth — see the Time & Weather doc for the day/night blend and the per-weather profiles (clear, fog, rain, snow).
Physically Grounded Density
density is not eyeballed — it's the extinction coefficient β in Koschmieder's Law, the standard atmospheric-optics formula relating extinction to meteorological visibility distance V (the range at which an object's contrast against the sky falls to the 2% threshold where it's considered to have vanished): β = -ln(0.02) / V ≈ 3.912 / V. That's exactly the shader's fog-factor exponent, so density = 3.912 / V reproduces a given real-world visibility exactly. Per-weather V is drawn from WMO and aviation-meteorology visibility categories, not toybox intuition:
| Weather | Visibility (V) | β = 3.912/V | Basis |
|---|---|---|---|
| Clear | 30 km | ≈1.3×10⁻⁶/cm | WMO's "clear" visibility threshold (≥30km) |
| Fog | 500 m | ≈7.8×10⁻⁵/cm | Moderate fog |
| Rain | 2 km | ≈2.0×10⁻⁵/cm | Moderate rain (heavy rain drops below 1km) |
| Snow | 800 m | ≈4.9×10⁻⁵/cm | Snowfall, commonly cited as cutting visibility below 1km |
Visibility V is where an object is effectively gone (2% contrast), not where haze first becomes noticeable — a clear day's 30km V still gives a soft atmospheric-perspective fade at the terrain's 5.12km far edge (~49% fog factor there), while nearby grass (under ~1km) reads with only light haze (~12%). This replaces an earlier tuning (clear ~50% fog at 35m) picked for the terrain's old ~51m-per-side scale, which read as a permanent haze once the terrain reached its real km scale — see
tweaks.ts'sFOG_BY_WEATHERfor the exact values this doc mirrors.
Palette
Buildings are colored by material family, not literally by zone type — avoids the "red = residential" cliché while keeping the world legible.
The MaterialFamily enum defines the valid material families for generated buildings:
enum MaterialFamily {
CorrugatedMetal = "corrugated-metal",
ConcreteBlock = "concrete-block",
Paint = "paint",
VinylSiding = "vinyl-siding",
Stucco = "stucco",
Timber = "timber",
WoodClapboard = "wood-clapboard",
Brick = "brick",
Stone = "stone",
CurtainWallGlass = "curtain-wall-glass",
}
Land value tiers
Materials are grouped by land value tier — economy-zoning is the single source of truth for the landValue formula and the tier boundaries; this doc only assigns materials to the resulting tier index (0–3), not to any numeric landValue range. The procedural-buildings plan's GenerationRule.materialsByLandValue selects a material from the array for the parcel's current tier; the same parcel visibly upgrades its facade as its land value rises. Two or three materials per tier give variety without confusing the cheap-vs-expensive signal.
| Tier | Materials |
|---|---|
| 0 (cheap) | Corrugated metal, Concrete block, Paint |
| 1 (low-mid) | Vinyl siding, Stucco, Timber |
| 2 (mid-high) | Wood clapboard, Brick |
| 3 (high) | Stone, Curtain wall glass |
Visual signals
Each material has 2–3 visual signals the procedural-materials shader pass must hit. The signals are what makes "cheap reads cheap at a glance" work — they're not just color, they're shape, mass, and texture.
- Corrugated metal — wavy, reflective, often rusted; thin profile. Reads as industrial / lean-to.
- Concrete block (CMU) — matte grey, visible block grid, geometric. Reads as utilitarian / cheap.
- Paint — a painted substrate (concrete, stucco, or wood) whose condition, not just its hue, is driven by the parcel's land value tier: at tier 0 it reads weathered, peeling, or flaking with the substrate showing through; at higher tiers the same material reads freshly painted and clean. See Procedural Materials for why tier 0 gets the weathered look rather than just a cheaper color.
- Vinyl siding — flat manufactured panels, regular horizontal seams every 6–12 inches, slightly "plastic-y" sheen. Reads as cheap suburban tract housing.
- Stucco — flat painted, smooth, no visible joints. Reads as warm mid-range residential / commercial.
- Timber — exposed wood: log construction, timber-frame, plank siding. Reads as rural or, in a timber-rich biome, as the regional default (see biomes note below).
- Wood clapboard — painted horizontal wooden boards, regular overlap, warm tones. Reads as traditional residential premium.
- Brick — warm reds / oranges / browns, regular coursing pattern, dimensional shadow lines. Reads as solid mid-to-high traditional.
- Stone — heavy, solid, premium tones (limestone, sandstone, granite). Reads as institutional / civic premium.
- Curtain wall glass — reflective, geometric grid, modern. Reads as contemporary commercial premium.
Color palette, noise pattern, and window rhythm for each material are owned by the Procedural Materials doc; the signals above are the silhouette-level commitment this doc makes.
Biomes (future work)
The list above is a single global set, but the same land value tier can read very differently in different regional contexts. A timber-rich biome should weight Timber upward; a desert biome should weight ConcreteBlock and Stucco. A future biomes pass would replace the global tier-to-material array with a biome-keyed variant. Out of scope for v1; deferred to a follow-up design pass.
Materials Tie-In
The base-color + noise-layer approach from the Procedural Materials doc is the candidate default for building facades and ground cover as well.
Photo Mode Tie-In
Photo Mode's time-of-day presets (Time & Weather doc) are the art-directed answer to the fixed screenshot lighting question below — Noon, Morning, and Golden Hour are just named `dayPhase` values driving this doc's one dynamic sun/sky, not a separate lighting path.