Skip to content

Level of detail: terrain LOD, instanced LOD, and hybrid cluster GAS #74

@brendancol

Description

@brendancol

Context

Roadmap Phase 2 item (see #57). Current rendering uses a single resolution for the entire terrain and full-detail geometry at all distances. This limits scene scale — large DEMs either run out of GPU memory or render slowly. LOD enables city-scale and regional scenes by reducing detail where the camera can't perceive it.

Goal

Three LOD systems that work together:

  1. Terrain LOD — distance-based subsampling of terrain tiles
  2. Instanced Geometry LOD — simplified meshes and billboard imposters for far objects
  3. Hybrid Cluster GAS — cluster-accelerated GAS for nearby geometry, simplified GAS for distant

1. Distance-Based Terrain LOD

Current State

_rebuild_at_resolution() applies a uniform subsample factor across the entire terrain. The chunk manager (_MeshChunkManager) loads/unloads spatial chunks but all at the same resolution.

Design

  • Divide terrain into tiles (reuse chunk grid or define LOD-specific tile size)
  • Assign LOD level per tile based on distance from camera to tile center
  • LOD 0 = full resolution, LOD 1 = 2x subsample, LOD 2 = 4x, etc.
  • Each LOD level is a pre-built or on-demand triangulate_terrain() at reduced resolution
  • Tile boundaries need skirt geometry or stitching to hide T-junction cracks between LOD levels (skirt approach already exists in codebase)
  • Update LOD assignments each frame (or on camera move beyond threshold)

Key Considerations

  • Chunk manager cache keyed by (chunk_id, lod_level) — existing cache invalidation logic applies
  • Z re-snapping for placed geometry must use the LOD-appropriate terrain (see MEMORY.md gotcha)
  • OptiX IAS instance transforms can reference different GAS per LOD tile

2. Instanced Geometry LOD

Current State

place_mesh() creates instanced geometry via IAS instance transforms pointing to shared GAS. All instances render the full mesh regardless of distance.

Design

Mesh LOD Levels

  • LOD 0: original mesh (full detail)
  • LOD 1: simplified mesh (e.g., 50% triangle reduction via quadric decimation)
  • LOD 2: low-poly proxy (e.g., bounding box or convex hull)
  • LOD 3: billboard imposter (camera-facing quad with pre-rendered texture)

Billboard Imposters

  • Pre-render each mesh from 8–16 azimuth angles × 2–3 elevation angles into a texture atlas
  • At runtime, select the closest viewing angle and render a textured quad
  • Imposters use a separate SBT hit group with alpha-tested texture lookup
  • Transition distance: where screen-space size of object drops below ~16px

Runtime Selection

  • Per-instance LOD selection based on distance to camera
  • IAS rebuild when LOD assignments change (batch updates, not per-frame)
  • Hysteresis band to prevent LOD flickering at boundaries

3. Hybrid Cluster GAS

Current State

Cluster GAS support exists (see MEMORY.md — OptiX 9.1 upgrade notes). Terrain can be partitioned into cluster blocks. Currently used uniformly.

Design

  • Near range (< threshold): cluster GAS — full BVH quality, best ray traversal for detailed close-up geometry
  • Far range (> threshold): simplified GAS — fewer triangles, standard (non-cluster) build for faster BVH construction
  • Threshold based on screen-space projected size or fixed distance bands
  • Both GAS types coexist in the same IAS with per-instance transforms
  • Cluster GAS blocks can double as terrain LOD 0 tiles

Transition Strategy

  • Hybrid boundary aligned with terrain LOD tile grid
  • Cross-fade or dither at transition boundary to hide pops (optional, may not be needed for terrain)
  • Rebuild schedule: cluster GAS is expensive to build, so rebuild only when camera moves significantly (>1 tile width)

Implementation Plan

  1. Terrain tile grid and LOD assignment — camera-distance metric, LOD level calculation, tile data structure
  2. Multi-resolution terrain GAS — build separate GAS per (tile, LOD), wire into IAS
  3. Tile boundary stitching — extend existing skirt approach or add edge-matched vertices
  4. Mesh LOD pipeline — mesh simplification (quadric decimation), generate LOD chain per mesh source
  5. Billboard imposter generation — offline pre-render pass, texture atlas, alpha-test hit group
  6. Per-instance LOD selection — distance bucketing, IAS instance LOD assignment, batch rebuild
  7. Hybrid cluster/standard GAS — distance-based GAS type selection, mixed IAS construction
  8. LOD budget system — global triangle/instance budget, auto-adjust LOD thresholds to hit target frame time
  9. Cache management — LOD-aware chunk cache, memory pressure eviction (farthest tiles first)

Scope Boundaries

  • No streaming from disk/network (LOD data is generated in-memory from source meshes/terrain)
  • No continuous LOD (CLOD) or geometry clipmaps — discrete LOD levels are sufficient
  • Billboard imposters are stretch goal — mesh LOD levels come first
  • No per-frame IAS rebuild; batch on camera movement threshold

References

  • Chunk manager: engine.py (_MeshChunkManager)
  • Terrain meshing: mesh.py (triangulate_terrain)
  • Cluster GAS: rtx.py cluster accel APIs, MEMORY.md notes
  • Skirt geometry: recent commit 9b30627
  • Resolution rebuild: engine.py (_rebuild_at_resolution())

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions