Smooth terrain: subdivision / tessellation surface through the height samples #211

Closed
opened 2026-09-30 21:16:57 +00:00 by sky_line · 2 comments
Owner

The Lightbox "Smooth terrain" checkbox (AlchemyRenderTerrainSmoothing) does nothing under Vulkan. Implement it as a GPU subdivision of the terrain patch.

Reference: upstream Alchemy GL (820dbaa0ab)

  • A tessellation evaluation stage evaluates a monotone bicubic (a Hermite patch per metre cell) through the same height samples, plus its analytic gradient, from 12 height texels.
  • Corner tangents are the mean of the neighbouring secants, limited Fritsch-Carlson style: at most 3x the smaller secant, and zero where the two secants disagree in sign. The value is held within the cell's four corners.
  • The result is C1, passes through every sample, and never overshoots. A cliff becomes an S-curve inside its own cell instead of a lip; Catmull-Rom was rejected because it grew metres of ridge on a 30 m cliff.
  • CPU mirror: ALTerrainSurfaceMaps::smoothHeight uses the same arithmetic, and LLSurface::resolveHeightRegion / resolveNormalGlobal switch to it, so picks, foot planting and parcel lines follow the drawn surface.
  • The simulator still collides against the linear surface. The two differ by less than one cell's rise.

Our plan

  • Subdivision: tessellation shader or mesh/compute pre-subdivision into the terrain VBO, with a distance-adaptive factor (full detail near the camera, none far away) so the cost stays bounded.
  • Surface: follow upstream's monotone bicubic; it has already solved the overshoot problem.
  • Consistency: keep the CPU mirror so picking and foot IK agree with what is drawn. Shadows, the depth pre-pass and the RT BLAS must all use the same surface.
  • Normals: analytic gradient into the terrain shading normal.
  • Region seams: neighbour height samples across region borders.

Refs the Lightbox cleanup (2375e1a183).

The Lightbox "Smooth terrain" checkbox (`AlchemyRenderTerrainSmoothing`) does nothing under Vulkan. Implement it as a GPU subdivision of the terrain patch. ## Reference: upstream Alchemy GL (820dbaa0ab) - A **tessellation evaluation stage** evaluates a **monotone bicubic** (a Hermite patch per metre cell) through the same height samples, plus its analytic gradient, from 12 height texels. - **Corner tangents** are the mean of the neighbouring secants, limited Fritsch-Carlson style: at most 3x the smaller secant, and zero where the two secants disagree in sign. The value is held within the cell's four corners. - The result is C1, passes through every sample, and never overshoots. A cliff becomes an S-curve inside its own cell instead of a lip; Catmull-Rom was rejected because it grew metres of ridge on a 30 m cliff. - **CPU mirror:** `ALTerrainSurfaceMaps::smoothHeight` uses the same arithmetic, and `LLSurface::resolveHeightRegion` / `resolveNormalGlobal` switch to it, so picks, foot planting and parcel lines follow the drawn surface. - The simulator still collides against the linear surface. The two differ by less than one cell's rise. ## Our plan - **Subdivision:** tessellation shader or mesh/compute pre-subdivision into the terrain VBO, with a distance-adaptive factor (full detail near the camera, none far away) so the cost stays bounded. - **Surface:** follow upstream's monotone bicubic; it has already solved the overshoot problem. - **Consistency:** keep the CPU mirror so picking and foot IK agree with what is drawn. Shadows, the depth pre-pass and the RT BLAS must all use the same surface. - **Normals:** analytic gradient into the terrain shading normal. - **Region seams:** neighbour height samples across region borders. Refs the Lightbox cleanup (2375e1a183).
Author
Owner

Landed on branch terraintess (commit ba64bf4d4e, not yet merged/built).

Surface: upstream's monotone bicubic (820dbaa0ab) ported to llterrainsmooth.h, same arithmetic. It passes exactly through every sample and never overshoots. Normals are its analytic gradient. LLSurface::resolveHeightRegion / resolveNormalGlobal switch to it while the setting is on (picks, foot planting, parcel lines). The stencil reads one grid into neighbour regions; with no neighbour it clamps, which gives a zero tangent like upstream.

Geometry: static smooth meshes per patch LOD level, all in the existing mesh store / LOD group:

  • level 0: 1 m with smooth normals;
  • level 1: 0.5 m, out to ~120 m;
  • level 2: 0.25 m, out to ~56 m (the default finest);
  • level 3: 0.125 m, out to ~12 m.

Every view uses the same surface with no new pass: main, depth pre-pass, shadow cascades, mirror, probes (which draw level 0), the per-slot RT BLAS and the pick boxes. Fine levels stay resident only near the camera: they're built nearest first under a per-frame vertex budget and freed with hysteresis. Edits are caught by a content hash and rebuilt with force.

LOD:

  • The cull and the TLAS pass both choose the level from the main camera's distance to the patch box.
  • The TLAS references the nearest level whose BLAS is already built, so a patch never leaves the TLAS.
  • Geomorph: each vertex stores its height step to the coarser level in texcoord0.y, and the VS blends it in over a band that ends at the switch distance. Switches don't pop, and finer edges meet coarser neighbours.
  • Skirts: each level has two-sided skirts, as deep as the steepest edge cell's rise + 5 cm. They cover leftover cracks: very tall patches, levels still loading, a neighbour region not yet arrived.

Settings:

  • RenderVKTerrainSmooth (default on, live). It has a checkbox in Graphics > Advanced under "Hex-tile terrain textures", and the Lightbox "Smooth terrain" checkbox is rebound to it. The dead AlchemyRenderTerrainSmoothing is removed. Off gives today's faceted terrain exactly.
  • RenderVKTerrainSmoothDetail (0-3, default 2).
  • Both are in the feature tables (Low off, LowMid 0.5 m, Mid+ 0.25 m) and in graphics presets.

Known:

  • The simulator still collides against the flat surface. The two differ by less than one 1 m cell's rise.
  • Tall cliff patches (more than ~15-24 m of relief) can micro-pop at a level switch. The skirts cover any crack.
  • The RT BLAS is the unmorphed level, so inside a morph band on steep ground it differs from the raster by up to that band's step.
Landed on branch `terraintess` (commit ba64bf4d4e, not yet merged/built). **Surface:** upstream's monotone bicubic (820dbaa0ab) ported to `llterrainsmooth.h`, same arithmetic. It passes exactly through every sample and never overshoots. Normals are its analytic gradient. `LLSurface::resolveHeightRegion` / `resolveNormalGlobal` switch to it while the setting is on (picks, foot planting, parcel lines). The stencil reads one grid into neighbour regions; with no neighbour it clamps, which gives a zero tangent like upstream. **Geometry:** static smooth meshes per patch LOD level, all in the existing mesh store / LOD group: - level 0: 1 m with smooth normals; - level 1: 0.5 m, out to ~120 m; - level 2: 0.25 m, out to ~56 m (the default finest); - level 3: 0.125 m, out to ~12 m. Every view uses the same surface with no new pass: main, depth pre-pass, shadow cascades, mirror, probes (which draw level 0), the per-slot RT BLAS and the pick boxes. Fine levels stay resident only near the camera: they're built nearest first under a per-frame vertex budget and freed with hysteresis. Edits are caught by a content hash and rebuilt with force. **LOD:** - The cull and the TLAS pass both choose the level from the main camera's distance to the patch box. - The TLAS references the nearest level whose BLAS is already built, so a patch never leaves the TLAS. - **Geomorph:** each vertex stores its height step to the coarser level in texcoord0.y, and the VS blends it in over a band that ends at the switch distance. Switches don't pop, and finer edges meet coarser neighbours. - **Skirts:** each level has two-sided skirts, as deep as the steepest edge cell's rise + 5 cm. They cover leftover cracks: very tall patches, levels still loading, a neighbour region not yet arrived. **Settings:** - `RenderVKTerrainSmooth` (default on, live). It has a checkbox in Graphics > Advanced under "Hex-tile terrain textures", and the Lightbox "Smooth terrain" checkbox is rebound to it. The dead `AlchemyRenderTerrainSmoothing` is removed. Off gives today's faceted terrain exactly. - `RenderVKTerrainSmoothDetail` (0-3, default 2). - Both are in the feature tables (Low off, LowMid 0.5 m, Mid+ 0.25 m) and in graphics presets. **Known:** - The simulator still collides against the flat surface. The two differ by less than one 1 m cell's rise. - Tall cliff patches (more than ~15-24 m of relief) can micro-pop at a level switch. The skirts cover any crack. - The RT BLAS is the unmorphed level, so inside a morph band on steep ground it differs from the raster by up to that band's step.
Author
Owner

Verified done: smooth terrain implemented under Vulkan: upstream's monotone bicubic surface, distance LOD with geomorph and skirts, CPU mirror for picks/foot planting, RT BLAS follows the morphed surface; RenderVKTerrainSmooth (Lightbox checkbox rebound). Commits: ba64bf4d4e, f7c3d9016c. Shipped in v1.0.0-beta.1.

Verified done: smooth terrain implemented under Vulkan: upstream's monotone bicubic surface, distance LOD with geomorph and skirts, CPU mirror for picks/foot planting, RT BLAS follows the morphed surface; RenderVKTerrainSmooth (Lightbox checkbox rebound). Commits: ba64bf4d4e, f7c3d9016c. Shipped in v1.0.0-beta.1.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
sky_line/Slipstream#211
No description provided.