Skip to content

Conversation

@SamHL
Copy link

@SamHL SamHL commented Dec 17, 2025

Screenshot 2025-12-17 at 7 03 20 pm Visual symptoms: - At certain camera orientations, large portions of the visible Earth (often an entire hemisphere) would suddenly disappear - The issue was orientation-sensitive: a small rotation of the camera would cause the missing terrain to reappear - Most noticeable when viewing the globe from orbital distances

Root cause:
The horizon culling in SurfaceNode::isVisible() was calling Horizon::isVisible() with radius=0, treating each tile's horizon culling point as an infinitesimal point rather than accounting for the tile's actual spatial extent. This caused tiles near the horizon boundary to be incorrectly culled even when part of the tile should have been visible.

Additionally, Horizon::isVisible() had a potential numerical precision issue where the calculation sqrt(dot(VT,VT) - a*a) could produce NaN if floating-point errors caused the value under the sqrt to go negative.

The fix:

  1. SurfaceNode.h: Pass the tile's bounding sphere radius to the horizon visibility test, making culling more conservative by accounting for the tile's actual size

  2. Horizon.cpp: Add numerical safeguards to prevent NaN propagation:

    • Clamp cSquared to >= 0 before taking sqrt
    • If the final result is NaN, default to treating tile as visible

Visual symptoms:
- At certain camera orientations, large portions of the visible Earth
  (often an entire hemisphere) would suddenly disappear
- The issue was orientation-sensitive: a small rotation of the camera
  would cause the missing terrain to reappear
- Most noticeable when viewing the globe from orbital distances

Root cause:
The horizon culling in SurfaceNode::isVisible() was calling
Horizon::isVisible() with radius=0, treating each tile's horizon
culling point as an infinitesimal point rather than accounting for
the tile's actual spatial extent. This caused tiles near the horizon
boundary to be incorrectly culled even when part of the tile should
have been visible.

Additionally, Horizon::isVisible() had a potential numerical precision
issue where the calculation `sqrt(dot(VT,VT) - a*a)` could produce NaN
if floating-point errors caused the value under the sqrt to go negative.

The fix:
1. SurfaceNode.h: Pass the tile's bounding sphere radius to the horizon
   visibility test, making culling more conservative by accounting for
   the tile's actual size

2. Horizon.cpp: Add numerical safeguards to prevent NaN propagation:
   - Clamp cSquared to >= 0 before taking sqrt
   - If the final result is NaN, default to treating tile as visible
@gwaldron
Copy link
Member

Hi Sam and thanks for the submission.

I haven't seen this problem; do you have steps to reproduce it?
Do your numerical fixes in Horizon.cpp make the problem go away by themselves?

I ask because the whole idea of the "horizon culling point" is to be a single point (without any radius) that can determine the visibility of the entire tile. Here's a reference article:
https://cesium.com/blog/2013/05/09/computing-the-horizon-occlusion-point/

It is definitely possible we are not calculating the point correctly and that is causing artifacts (though I have not seen this problem personally).

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.

2 participants