Skip to content

Frustum culling in cull_objects_outside_all_frustums incorrectly hides large/close objects that fill the camera frame #3

Description

@haimsaw

During the multi-view export (the operator that calls cull_objects_outside_all_frustums), objects that are clearly visible in a camera's rendered frame are sometimes still culled (hide_render = True) because the visibility test only samples discrete points, not the object's full extent.

is_object_in_camera_frustum() tests only:

Each of the object's 8 bounding-box corners, and
The object's origin/center point

against the camera frustum planes, returning True only if one of those specific points lies inside the frustum.

This fails for large, thin, close-up geometry — most obviously walls. When a camera is close to (or aimed straight at) a wall, the wall's face fills the entire frame, but its bounding-box corners can all lie outside the frustum's angular bounds (past the left/right/top/bottom edges), and its origin can likewise fall outside the frustum test. The function concludes the object is "not visible from any camera" and sets hide_render = True on it, even though it's plainly visible in the rendered image.

Steps to reproduce
Build a room with wall geometry (large planes) and position/aim a training camera so it's close to and facing one wall head-on, such that the wall fills the camera's field of view.
Run the multi-view export operator (the one that calls cull_objects_outside_all_frustums).
Inspect the exported render / the object's hide_render state afterward.
Expected behavior

Any object that is visibly rendered from at least one training camera should not have hide_render set to True.

Actual behavior

The wall is hidden from render (hide_render = True) even though it's directly in frame for at least one camera, because none of its bounding-box corners or its center point individually satisfy the point-in-frustum test.

Root cause

In is_object_in_camera_frustum():

python
bbox_corners = [obj.matrix_world @ Vector(corner) for corner in obj.bound_box]

for corner in bbox_corners:
if _check_point_in_frustum(corner, cam_loc, cam_forward, cam_right, cam_up,
clip_start, clip_end, cam_data, aspect, fov, half_width, half_height):
return True

obj_center = obj.matrix_world.translation
return _check_point_in_frustum(obj_center, cam_loc, cam_forward, cam_right, cam_up,
clip_start, clip_end, cam_data, aspect, fov, half_width, half_height)

This is a point-sampling test, not a true box/frustum intersection test. It can't detect the case where the camera frustum is entirely contained within the object's silhouette (frustum inside the box, rather than box corners inside the frustum) — which is exactly what happens when a camera is close to a large flat surface like a wall.

Suggested fix

Replace the point-sampling check with a proper AABB/frustum intersection test (e.g. a separating-axis test against the 6 frustum planes), or at minimum add a symmetric check: if none of the object's points are inside the frustum, also test whether the frustum's near-plane corners (projected into world space) fall inside the object's bounding box, to catch the "frustum inside object" case.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions