Skip to content

[Lighting Overhaul][Umbrella] Rebuild voxel lighting, shadows, and scene composition #898

Description

@MichaelFisher1997

Goal

Rebuild ZigCraft's lighting and shadow pipeline around a coherent, testable architecture that delivers a cinematic saturated look, readable-dark caves, stable shadows, and a 60 FPS target at 1080p on midrange discrete GPUs.

This issue is the canonical tracker for the audit and phased overhaul. It supersedes the implementation plan in #143: several features proposed there now exist, but the active render path bypasses them and the underlying correctness problems remain.

Target

  • Cinematic, saturated art direction without a global cyan cast.
  • Dark caves that remain spatially readable near entrances and after adaptation.
  • One lighting model across all graphics presets; presets vary cost, not semantics.
  • Existing worlds remain loadable and are relit when the lighting algorithm version changes.
  • LPV remains quarantined/experimental until the baseline pipeline is correct and within budget.
  • High preset targets 60 FPS at 1080p on designated midrange hardware.

Audit summary

Severity Finding Evidence
Critical Cave entrance illumination is generated and uploaded, but its intensity is ignored by the active shader. Zero sky/block light becomes exact black. assets/shaders/vulkan/terrain.frag:460-515
Critical simple_lighting_mode is hard-coded on, disabling PBR, SSAO, LPV, and volumetrics regardless of presets. modules/game-ui/src/screens/world.zig:355-360, world_frame_params.zig:46-95
Critical Initial sky and RGB block light do not cross chunk boundaries, creating seams and load-order-dependent results. modules/worldgen-common/src/lighting_computer.zig:98-138,318-358
Critical Runtime relighting is bounded by the edited chunk edge rather than light influence, leaving stale neighboring light. modules/world-runtime/src/lighting_engine.zig:23-70,195-245
High Relighting erases entrance-light data without rebuilding it; persistence omits it. lighting_engine.zig:94-104, chunk_serializer.zig:15-25
High Chunk mutation and asynchronous meshing can race and publish a mixed-revision mesh. world_mutation.zig:51-81, chunk_queue_coordinator.zig:493-589
High CSM fitting uses frustum depth while shader selection uses radial distance and a hard-coded 72-128 unit fade. csm.zig:92-110, terrain.frag:680-727
High Runtime shadow quality changes shader math without recreating the physical shadow image. rhi_resource_setup.zig:25-67, render_graph.zig:264-293
High Cutout casters are not alpha-tested; vegetation casts solid rectangles or no shadow. shadow.vert:43-52, shadow.frag:1-9
High Sky and CPU shadow UBO layouts disagree: sky declares three matrices but can index the fourth cascade. sky.frag:34-69, rhi_shadow_bridge.zig:4-9
High UNORM/headless output does not perform a format-independent display encoding, crushing dark values. post_process.frag:72-104, vulkan_swapchain.zig:119-192
High Terrain, atmosphere, water, and mandatory grading inject separate cyan/saturation terms. terrain.frag:489-503, atmosphere/config.zig:10-15, post_process.frag:107-115
Medium PBR view vectors mix absolute camera coordinates with camera-relative fragments. terrain.frag:767-773
Medium SSAO combines view-space positions with world-space normals and uses a non-bilateral blur. ssao.frag:18-76
Medium GPU meshing emits an incompatible vertex layout and hard-codes full skylight. mesh.comp:30-37,188-199

Architectural decision

Keep the Vulkan resource ownership, render graph, reverse-Z convention, atmosphere timing, and packed 4-bit skylight/RGB block-light storage.

Rewrite these cohesive units rather than tuning around their contradictions:

  • Authoritative world-level voxel light propagation and mutation updates.
  • CSM fitting, selection, overlap, bias, caster bounds, and filtering.
  • Terrain lighting composition and the final scene-linear/display transform contract.

Target data flow:

blocks
  -> WorldLightingEngine
  -> stable PackedLight + chunk revision
  -> immutable meshing snapshot
  -> atmosphere-driven PBR-lite composition
  -> linear HDR
  -> one tone mapper
  -> exactly one sRGB display encoding

Phase tracking

Child issues are ordered dependencies and should land as small PR series targeting dev.

Global acceptance criteria

  • No black cave holes where valid propagated skylight reaches the space.
  • Enclosed caves remain dark but readable without forced gray-blue desaturation.
  • No light seams across chunk boundaries or changes caused by chunk load order.
  • Runtime block placement/removal updates all affected loaded chunks.
  • No visible shadow swimming, cascade lines, detached contacts, or rectangular foliage shadows.
  • Windowed and headless captures match in midtone and color response.
  • Terrain, sky, fog, water, and volumetrics share one atmosphere/color contract.
  • High preset meets the agreed 1080p/60 FPS target on designated midrange hardware.
  • Shadow GPU time is targeted below 2.5 ms, SSAO below 1 ms, and optional volumetrics below 2 ms.
  • LPV has no allocation or frame cost while quarantined/disabled.

Constraints

  • Preserve existing worlds; relight derived data on load rather than invalidating block data.
  • Preserve worker-thread RHI isolation.
  • Do not introduce a second lighting path for individual presets.
  • Keep GPU meshing disabled until its data contract and lighting output match the CPU path.
  • All build/test commands must be wrapped in nix develop --command.
  • Graphics PRs require shader validation, an offscreen world-load check, deterministic screenshots, and a benchmark comparison.

Baseline verification

nix develop --command zig build test
nix develop --command zig build -Dskip-present
nix develop --command zig build test-robustness
nix develop --command zig build run -Dskip-present -Dshadow-test-scene -Dscreenshot-path=screenshots/shadow-test.png -Dscreenshot-frame=180
nix develop --command zig build benchmark -Doptimize=ReleaseFast -Dbenchmark-preset=high -Dbenchmark-duration=60 -Dbenchmark-output=benchmark-high-release.json

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions