Skip to content

Commit 6b496d8

Browse files
Moultclaude
andcommitted
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>
1 parent c0e99a1 commit 6b496d8

File tree

1 file changed

+15
-1
lines changed

1 file changed

+15
-1
lines changed

src/ifcviewer/ViewportWindow.cpp

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1388,7 +1388,21 @@ void ViewportWindow::cullModelCpu(ModelGpuData& m, const float planes[6][4],
13881388
// HiZ occlusion is skipped entirely when the pick pass runs
13891389
// (min_pixel_radius == 0 on that path), when the user disables it via
13901390
// env var, or before the first pyramid has been built.
1391-
const bool hiz_on = hizEnabled() && min_pixel_radius > 0.0f && hiz_vp_valid_;
1391+
//
1392+
// Crucially, HiZ is also skipped when the stored VP (hiz_vp_, captured at
1393+
// the end of the previous frame) differs from this frame's VP — i.e.
1394+
// whenever the camera has moved. The stored depth buffer encodes what
1395+
// was visible from hiz_vp_'s viewpoint; projecting a current-frame AABB
1396+
// through that VP answers "was this occluded LAST frame?", which is only
1397+
// a correct proxy for "is this occluded NOW?" when the camera is static.
1398+
// Orbiting past a wall would otherwise leave objects persistently culled
1399+
// because prior frames' depth buffers only ever contained the wall (the
1400+
// objects behind it were themselves HiZ-culled, never drawn, so never in
1401+
// the buffer — a self-reinforcing feedback loop). On static views HiZ
1402+
// kicks in after a single frame of lag.
1403+
const QMatrix4x4 current_vp = proj_matrix_ * view_matrix_;
1404+
const bool hiz_vp_matches = hiz_vp_valid_ && hiz_vp_ == current_vp;
1405+
const bool hiz_on = hizEnabled() && min_pixel_radius > 0.0f && hiz_vp_matches;
13921406

13931407
// Hot path: read the AABB from the compact bvh_items array (28 B stride)
13941408
// rather than the wide InstanceCpu (104 B stride). Most instances fail

0 commit comments

Comments
 (0)