ifcviewer: disable HiZ cull when camera has moved

HiZ from last frame encodes depth from last frame's viewpoint. When
the camera moves, projecting a current-frame AABB through the stored
VP answers 'was this occluded last frame?' rather than 'is it occluded
now?' — a self-reinforcing feedback loop where objects culled in
prior frames never appear in any depth buffer and stay permanently
hidden at certain camera angles.

Fix: require hiz_vp_ == current VP for the HiZ test to apply. HiZ
still helps static views (kicks in one frame after camera stops) but
no longer produces false occlusions during orbit. The correct fix for
orbit coverage is a depth pre-pass feeding fresh HiZ — planned as
part of Phase 3E GPU compute cull.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Dion Moult
2026-04-15 17:46:47 +10:00
parent 0c9d3ea6d7
commit 99f409280a
+15 -1
View File
@@ -1388,7 +1388,21 @@ void ViewportWindow::cullModelCpu(ModelGpuData& m, const float planes[6][4],
// HiZ occlusion is skipped entirely when the pick pass runs
// (min_pixel_radius == 0 on that path), when the user disables it via
// env var, or before the first pyramid has been built.
const bool hiz_on = hizEnabled() && min_pixel_radius > 0.0f && hiz_vp_valid_;
//
// Crucially, HiZ is also skipped when the stored VP (hiz_vp_, captured at
// the end of the previous frame) differs from this frame's VP — i.e.
// whenever the camera has moved. The stored depth buffer encodes what
// was visible from hiz_vp_'s viewpoint; projecting a current-frame AABB
// through that VP answers "was this occluded LAST frame?", which is only
// a correct proxy for "is this occluded NOW?" when the camera is static.
// Orbiting past a wall would otherwise leave objects persistently culled
// because prior frames' depth buffers only ever contained the wall (the
// objects behind it were themselves HiZ-culled, never drawn, so never in
// the buffer — a self-reinforcing feedback loop). On static views HiZ
// kicks in after a single frame of lag.
const QMatrix4x4 current_vp = proj_matrix_ * view_matrix_;
const bool hiz_vp_matches = hiz_vp_valid_ && hiz_vp_ == current_vp;
const bool hiz_on = hizEnabled() && min_pixel_radius > 0.0f && hiz_vp_matches;
// Hot path: read the AABB from the compact bvh_items array (28 B stride)
// rather than the wide InstanceCpu (104 B stride). Most instances fail