Skip to content

Add example: avoid jitter in a custom layer at high zoom (#4871)#7946

Draft
clement-igonet wants to merge 5 commits into
maplibre:mainfrom
clement-igonet:example/avoid-jitter-in-custom-layer-4871
Draft

Add example: avoid jitter in a custom layer at high zoom (#4871)#7946
clement-igonet wants to merge 5 commits into
maplibre:mainfrom
clement-igonet:example/avoid-jitter-in-custom-layer-4871

Conversation

@clement-igonet

@clement-igonet clement-igonet commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

Fixes #4871 (custom layer jitter at high zoom) with a documentation example. The jitter comes from storing absolute mercator coordinates in float32 vertex buffers. The example shows origin-relative rendering: store small offsets from a fixed origin, multiply the origin through the 64-bit defaultProjectionData.mainMatrix on the CPU, and add the two in the shader. A toggle compares this against the naive approach.

Launch Checklist

  • Confirm your changes do not include backports from Mapbox projects
  • Link to related issues
  • Include before/after visuals (gallery thumbnail)
  • Confirm you have read our AI policy

Assisted-By: Claude Sonnet 5 (claude-sonnet-5)

@clement-igonet
clement-igonet marked this pull request as ready for review July 15, 2026 00:00
@codecov

codecov Bot commented Jul 15, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 93.24%. Comparing base (4435459) to head (dfdd39f).
⚠️ Report is 5 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #7946      +/-   ##
==========================================
- Coverage   93.25%   93.24%   -0.01%     
==========================================
  Files         290      290              
  Lines       24650    24650              
  Branches     6471     6471              
==========================================
- Hits        22987    22986       -1     
- Misses       1663     1664       +1     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

clement-igonet added a commit to clement-igonet/maplibre-gl-js that referenced this pull request Jul 15, 2026
Proof of concept for the phasing proposed on maplibre#4493 ("Add 3D Tiles as
a source for the terrain layer"): decodes real quantized-mesh binary
tiles (header, vertex data, triangle/edge indices - the format used
by Cesium/3D Tiles terrain) with no library dependency, and renders
them via a custom layer using the origin-relative rendering technique
from maplibre#4871/maplibre#7946.

Uses a 5x5 block (25 tiles, zoom 12) of real Cesium World Terrain
data over the Mont Blanc massif so the demo is checkable against
reality rather than a synthetic fixture. Normalizes shading color
against one height range shared across every tile (rather than each
tile's own local quantized range) to avoid a visible shading seam at
tile boundaries. Pins five named summits (Mont Blanc, Aiguille du
Midi, Mont Blanc du Tacul, Dôme du Goûter, Aiguille du Plan) with
floating labels, each rendered/positioned in the same origin-relative
coordinate space as the terrain mesh so they track the actual sampled
elevation instead of a flat ground plane. Raises maxPitch to 85 to
allow a more dramatic viewing angle.

Deliberately out of scope for this phase: dynamic LOD tile
selection, elevation queries, layer draping, and occlusion-aware
tile throttling - those belong to the "first valuable feature" and
"hardening" phases proposed on the issue.
clement-igonet added a commit to clement-igonet/maplibre-gl-js that referenced this pull request Jul 15, 2026
Proof of concept for the phasing proposed on maplibre#4493 ("Add 3D Tiles as
a source for the terrain layer"): decodes real quantized-mesh binary
tiles (header, vertex data, triangle/edge indices - the format used
by Cesium/3D Tiles terrain) with no library dependency, and renders
them via a custom layer using the origin-relative rendering technique
from maplibre#4871/maplibre#7946.

Uses a 2x2 block (4 tiles, zoom 10) of real Cesium World Terrain data
covering the whole Mont Blanc massif, so the demo is checkable against
reality rather than a synthetic fixture, while keeping the bundled
tile count small. Normalizes shading color against one height range
shared across every tile (rather than each tile's own local quantized
range) to avoid a visible shading seam at tile boundaries. Pins five
named summits (Mont Blanc, Aiguille du Midi, Mont Blanc du Tacul,
Dôme du Goûter, Aiguille du Plan) with floating labels, each
rendered/positioned in the same origin-relative coordinate space as
the terrain mesh so they track the actual sampled elevation instead
of a flat ground plane. Raises maxPitch to 85 to allow a more
dramatic viewing angle.

Deliberately out of scope for this phase: dynamic LOD tile
selection, elevation queries, layer draping, and occlusion-aware
tile throttling - those belong to the "first valuable feature" and
"hardening" phases proposed on the issue.
clement-igonet added a commit to clement-igonet/maplibre-gl-js that referenced this pull request Jul 15, 2026
Proof of concept for the phasing proposed on maplibre#4493 ("Add 3D Tiles as
a source for the terrain layer"): decodes a real quantized-mesh binary
tile (header, vertex data, triangle/edge indices - the format used
by Cesium/3D Tiles terrain) with no library dependency, and renders
it via a custom layer using the origin-relative rendering technique
from maplibre#4871/maplibre#7946.

Uses a single tile (zoom 9) of real Cesium World Terrain data
covering the whole Mont Blanc massif, so the demo is checkable
against reality rather than a synthetic fixture, while keeping the
bundled data to the minimum needed. Pins five named summits (Mont
Blanc, Aiguille du Midi, Mont Blanc du Tacul, Dôme du Goûter,
Aiguille du Plan) with floating labels, each rendered/positioned in
the same origin-relative coordinate space as the terrain mesh so
they track the actual sampled elevation instead of a flat ground
plane. Raises maxPitch to 85 to allow a more dramatic viewing angle.

Deliberately out of scope for this phase: dynamic LOD tile
selection, elevation queries, layer draping, and occlusion-aware
tile throttling - those belong to the "first valuable feature" and
"hardening" phases proposed on the issue.
clement-igonet added a commit to clement-igonet/maplibre-gl-js that referenced this pull request Jul 15, 2026
Proof of concept for the phasing proposed on maplibre#4493 ("Add 3D Tiles as
a source for the terrain layer"): decodes a real quantized-mesh binary
tile (header, vertex data, triangle/edge indices - the format used
by Cesium/3D Tiles terrain) with no library dependency, and renders
it via a custom layer using the origin-relative rendering technique
from maplibre#4871/maplibre#7946.

Uses a single tile (zoom 9) of real Cesium World Terrain data
covering the whole Mont Blanc massif, so the demo is checkable
against reality rather than a synthetic fixture, while keeping the
bundled data to the minimum needed. Pins five named summits (Mont
Blanc, Aiguille du Midi, Mont Blanc du Tacul, Dôme du Goûter,
Aiguille du Plan) with floating labels, each rendered/positioned in
the same origin-relative coordinate space as the terrain mesh so
they track the actual sampled elevation instead of a flat ground
plane. Raises maxPitch to 85 to allow a more dramatic viewing angle.

Deliberately out of scope for this phase: dynamic LOD tile
selection, elevation queries, layer draping, and occlusion-aware
tile throttling - those belong to the "first valuable feature" and
"hardening" phases proposed on the issue.
clement-igonet added a commit to clement-igonet/maplibre-gl-js that referenced this pull request Jul 16, 2026
Proof of concept for the phasing proposed on maplibre#4493 ("Add 3D Tiles as
a source for the terrain layer"): decodes a real quantized-mesh binary
tile (header, vertex data, triangle/edge indices - the format used
by Cesium/3D Tiles terrain) with no library dependency, and renders
it via a custom layer using the origin-relative rendering technique
from maplibre#4871/maplibre#7946.

Uses a single tile (zoom 9) of real Cesium World Terrain data
covering the whole Mont Blanc massif, so the demo is checkable
against reality rather than a synthetic fixture, while keeping the
bundled data to the minimum needed. Pins five named summits (Mont
Blanc, Aiguille du Midi, Mont Blanc du Tacul, Dôme du Goûter,
Aiguille du Plan) with floating labels, each rendered/positioned in
the same origin-relative coordinate space as the terrain mesh so
they track the actual sampled elevation instead of a flat ground
plane. Raises maxPitch to 85 to allow a more dramatic viewing angle.

Deliberately out of scope for this phase: dynamic LOD tile
selection, elevation queries, layer draping, and occlusion-aware
tile throttling - those belong to the "first valuable feature" and
"hardening" phases proposed on the issue.

@CommanderStorm CommanderStorm left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like you've only added the example rather than the implementation.

Please have a look at the code before proposing this change. Based on the implementation, I don't think this is an appropriate example to add.

@CommanderStorm
CommanderStorm marked this pull request as draft July 18, 2026 21:33
@clement-igonet

Copy link
Copy Markdown
Contributor Author

Thanks for the look!

The missing implementation is actually the point. Per #4871, the jitter is from float32 precision limits in custom layers storing geometry in global mercator coords, and there's no planned core fix since the origin-relative technique solves it in user code.

The example is meant to document that pattern (the naive/origin-relative toggle shows the difference), like some other examples that teach a technique rather than an API.

If an example isn't the right place though, happy to move it into a short dev-guides section or close it in favor of an issue comment.

@CommanderStorm

Copy link
Copy Markdown
Member

idk, lets let @HarelM decide what to do here once he gets back into the office

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Custom Layer jumping at high zoom level

2 participants