fix(core): add shader-based globe occlusion for IconLayer and TextLayer#9975
fix(core): add shader-based globe occlusion for IconLayer and TextLayer#9975chrisgervang wants to merge 5 commits intomasterfrom
Conversation
charlieforward9
left a comment
There was a problem hiding this comment.
Looks like a nice cleanup for dependent projects building workarounds to deal with this.
A UX issue ive had to accept with the Text culling issue is not culling the GlobeView, which leads to this:
20260203-2059-29.4561020.mp4
Ill pull this version into my repo and test it out.
(is there an easier way for production builds to depend on deck.gl feature branches alternative to the submodule setup?)
felixpalmer
left a comment
There was a problem hiding this comment.
My hunch still is that there should be a way to fix this by setting up the camera parameters (#9592 (comment)) correctly - but for now this does resolve the issue.
In general we have a number of z-indexing issues when integrating the the maplibre globe, so perhaps in the future we'll be able to safely remove this again
|
|
||
| // Helper function to check if position is on the back of the globe | ||
| bool project_globe_is_occluded(vec3 commonPosition) { | ||
| return project_globe_get_occlusion(commonPosition) > 0.5; |
There was a problem hiding this comment.
seems a waste to do return visibility > 0.0 ? 0.0 : 1.0; only to undo it with project_globe_get_occlusion(commonPosition) > 0.5
There was a problem hiding this comment.
Thanks for catching that. I was exploring the idea of fading around the horizon and this got left over since I decided to start with a simple fix for now
| gl_Position = curr + vec4(project_pixel_size_to_clipspace(offset.xy), 0.0, 0.0); | ||
|
|
||
| // Hide arc segments that are occluded by the globe (on the back side) | ||
| if (project_globe_is_occluded(geometry.position.xyz)) { |
There was a problem hiding this comment.
I worry that this will cause artifacts when one end of a line segment will be occluded, while the other isn't. Instead you could set the widthPixels to 0 when occluded
There was a problem hiding this comment.
Good call, I'll switch to doing that
abe4b3c to
79f4262
Compare
|
Here's what the maplibre website example and get-started maplibre globe examples now look like without
|
@chrisgervang actually, no it looks broken to me as the flights are getting cut off too early. The current code does it correctly: Screen.Recording.2026-02-09.at.11.41.02.movwith the shader addition in this PR, we are losing the flights near the horizon - so I'm thinking this approach isn't going to work :/ Actually, I'm a bit confused why this PR needs to fix |
|
I had an approach in my workaround that blended opacity-fading with a more generous occlusion threshold for smoother transition - but I agree the ArcLayer is a difficult edge case since it is not a layer that sits on the surface of the globe. |
|
Ah true, we can remove ArcLayer from the scope of this PR. I included it because it had the But looking again, those were setting it to "none" which the system does again anyways now I'm removing the "back" override in |
|
@copilot remove ArcLayer from scope |
|
@charlieforward9 I've opened a new pull request, #10018, to work on those changes. Once the pull request is ready, I'll request review from you. |
… and ArcLayer Fixes #9777 and #9592. The previous approach of setting cullMode: 'back' globally for globe projections had two issues: 1. Billboard geometry (IconLayer, TextLayer) doesn't have proper back faces, causing icons and text to be incorrectly culled and disappear 2. The coordinate system handedness could cause culling to work in unexpected ways This change implements shader-based globe occlusion that works for all geometry types: - Added project_globe_get_occlusion() and project_globe_is_occluded() functions to the projection shader module (both GLSL and WGSL) - IconLayer, TextLayer (via MultiIconLayer), and ArcLayer now automatically hide geometry that is on the back side of the globe - Removed the global cullMode: 'back' parameter from getDefaultParameters() in deck-utils.ts - Updated examples to remove manual cullMode: 'none' workarounds The solution is transparent to users - no API changes or special configuration needed. https://claude.ai/code/session_01P6canyQPTd6nckN66pauLK
Adds a billboard dimension to basemap-browser example to test IconLayer and TextLayer behavior with billboard: true vs false on globe projection. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Simplify project_globe_is_occluded to return bool directly (remove redundant float conversion via project_globe_get_occlusion) - Fix GLSL icon-layer to use anchor position without offset for occlusion check, matching WGSL behavior - Fix arc-layer to set widthPixels=0 when occluded instead of hard clipping vertices, avoiding artifacts at segment boundaries Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
TextBackgroundLayer was missing the project_globe_is_occluded check, causing ghost background rectangles to appear on the back of the globe while the text itself was hidden. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
ArcLayer occlusion will be addressed separately. The depth buffer handles most cases for arc geometry, and the segment boundary artifacts need more investigation. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
458ff97 to
7d342b7
Compare
| <> | ||
| <DeckGL | ||
| controller | ||
| parameters={{cullMode: 'back'}} |
There was a problem hiding this comment.
Removed globe culling from standalone projection test app
Low Severity
Removing parameters={{cullMode: 'back'}} from the standalone DeckGL component causes a regression for the globe view in this test app. The app uses ScatterplotLayer, which has no shader-based globe occlusion. In overlaid mode (not interleaved), there's no shared depth buffer from the basemap, so scatter points on the back of the globe will now be visible — the only prior occlusion mechanism was the cullMode: 'back' parameter.
There was a problem hiding this comment.
GlobeView.tranparent.buffer.mov
If it looks like this old screen recording - I consider that a feature demo given the non interleaved config
There was a problem hiding this comment.
Good point. It looks like this example could be rewritten to use interleaved, or the cullMode config should be kept
| interleaved: boolean; | ||
| globe: boolean; | ||
| multiView: boolean; | ||
| billboard: boolean; |
There was a problem hiding this comment.
Unused globe field in LayerBuildOptions type
Low Severity
The globe field remains in the LayerBuildOptions type and is still passed by buildConfig, but the PR removed its only usage (for arcParameters) without removing the field itself. globe is never destructured or referenced in buildLayers, making it dead code that could mislead readers into thinking globe-awareness is handled in the layer building logic.




Fixes #9777 and #9592.
Problem
The previous approach of setting cullMode: 'back' globally for globe projections had two issues:
Solution
This change implements shader-based globe occlusion for billboard geometry:
project_globe_is_occluded()function to the projection shader module (both GLSL and WGSL)The solution is transparent to users - no API changes or special configuration needed.
When to use
project_globe_is_occluded()In interleaved mode with basemaps (MapLibre, Mapbox), the basemap's depth buffer provides occlusion for most geometry automatically. The shader-based
project_globe_is_occluded()function is needed for billboard geometry where screen-space pixel offsets bypass the depth test - the anchor point may be behind the globe while the visual appears in front.Usage Pattern
For billboard layers, use the anchor position without pixel offsets:
https://claude.ai/code/session_01P6canyQPTd6nckN66pauLK
Note
Medium Risk
Touches core projection shader code and vertex shaders for
IconLayer/TextLayerrendering on globe projection, which could affect visibility/positioning across WebGL and WebGPU backends.Overview
Adds shader-based globe occlusion for billboarded geometry by introducing
project_globe_is_occluded()in the projection shader module (GLSL + WGSL) and using it inIconLayer(GLSL + WGSL) andTextBackgroundLayervertex shaders to clip icons/text that are on the back side of the globe.Removes reliance on GPU back-face culling for globe mode by dropping the automatic
cullMode: 'back'from Mapbox/MapLibre integration (getDefaultParameters) and cleaning up example/test workarounds that set specialcullModevalues; the basemap-browser example also adds abillboarddimension + UI/URL toggle to control icon/text billboarding.Written by Cursor Bugbot for commit 7d342b7. This will update automatically on new commits. Configure here.