feat(geo-layers): TerrainLayer GlobeView + grid tesselator#10244
Closed
charlieforward9 wants to merge 1 commit intovisgl:masterfrom
Closed
feat(geo-layers): TerrainLayer GlobeView + grid tesselator#10244charlieforward9 wants to merge 1 commit intovisgl:masterfrom
charlieforward9 wants to merge 1 commit intovisgl:masterfrom
Conversation
Add a grid tesselator option to TerrainLayer and route the rendered mesh
through the correct coordinate system for each projection. Three pieces:
1. `tesselator: 'grid'` emits a fixed-resolution lng/lat/elev mesh in-process
(no worker), keeping the same cached mesh valid on both MapView and
GlobeView. The default `'auto'` path continues to use Martini/Delatin via
`@loaders.gl/terrain`. Grid rows are uniform in Mercator-y so they line
up with heightmap rows at any latitude (no polar sampling warp).
Two quality touches on the grid path:
- Elevation samples are clamped to [-500, 9000]m. Terrain-RGB encodes into
24 bits across three channels, so one corrupt pixel decodes to millions
of meters and renders as a specular star.
- Edge-vertex skirts are clamped to ≤1% of the in-tile grid step so the
seam slope stays imperceptible at any zoom (default 8m skirt over a
~0.6m grid step at z=21 was a visible cliff).
2. renderSubLayers picks the coordinate system per path: LNGLAT for the grid
tesselator (vertices are lng/lat) and on GlobeView for the legacy path
(GlobeViewport.projectFlat is identity so bounds land in lng/lat degrees),
CARTESIAN on MapView for the legacy path (bounds in Mercator world units).
Fixes the MapView→GlobeView case where legacy cartesian meshes landed
inside the sphere and were invisible.
3. Default material changes from `true` (PBR with specular) to a matte
configuration. Specular on terrain meshes catches as star-shaped glints
on skirt edges and elevation discontinuities — a matte surface reads as
a fluid landscape.
Also adds horizon culling in the tile-2d traversal used by the internal
TileLayer. The frustum test accepts tiles on the far hemisphere of the
globe; without a horizon check, low-zoom traversal fans out across the
entire globe. A point on the sphere is visible from camera C iff
dot(P, C) > |P|² — apply to the tile-vs-camera closest point (with lng
wrap so the clamp is angular, not Euclidean).
Docs updated for the new `tesselator` and `gridSize` props.
This was referenced Apr 19, 2026
Collaborator
Author
|
Superseded by #10250 (same branch re-opened under |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Makes
TerrainLayerrender correctly onGlobeViewand adds a grid tesselator that stays valid across projection toggles.Three related changes:
tesselator: 'grid'(opt-in) — emits a fixed-resolution lng/lat/elev mesh in-process (no worker). The default'auto'path keeps using Martini/Delatin via@loaders.gl/terrain. Grid rows are uniform in Mercator-y so they line up with heightmap rows at any latitude (no polar sampling warp). Because vertices are lng/lat, the same cached mesh renders correctly on bothMapViewandGlobeView— no re-tesselation on projection toggle.renderSubLayers— picksLNGLATfor the grid tesselator (vertices are lng/lat) and for the legacy path onGlobeView(whereGlobeViewport.projectFlatis identity, so bounds land in lng/lat degrees). UsesCARTESIANfor the legacy path onMapView. Previously, legacy cartesian meshes landed inside the sphere onGlobeViewand were invisible.tile-2d-traversal— the frustum test accepts tiles on the far hemisphere of the globe; without a horizon check, low-zoom traversal fans out across the whole globe and loads many tiles that can't be visible. A point on the sphere is visible from camera C iffdot(P, C) > |P|^2— applied to the tile-vs-camera closest point (with lng wrap so the clamp is angular, not Euclidean).Two quality touches on the grid path:
meshMaxError * 2(~8m) over a ~0.6m grid step at z=21 produced a near-vertical cliff at every tile edge.Default
materialchanges fromtrue(PBR with specular) to a matte configuration. Specular on terrain meshes catches as star-shaped glints on skirt edges and elevation discontinuities; matte reads as a fluid landscape.Why
Grid tesselation makes terrain cheap enough to keep cached on projection toggle, and solves the polar sampling warp that lat-uniform tesselators exhibit near the poles. The
LNGLATcoord-system fix and horizon culling are prerequisites for usingTerrainLayerunderGlobeViewat all.Test plan
tesselator: 'auto'— unchanged behaviortesselator: 'grid'— renders equivalently, single cache across zoom levelstesselator: 'grid'— renders correctly from z=0 through z=21+tesselator: 'auto'— renders (via the newLNGLATbranch)Companion PRs