Frustum culling

Пирамида отсечения (view frustum) и её роль в рендеринге

Визуальная сцена в CesiumJS строится вокруг камеры, которая определяет область видимости — усечённую пирамиду (frustum). Эта геометрическая фигура задаётся шестью плоскостями:

  • ближняя (near)
  • дальняя (far)
  • левая
  • правая
  • верхняя
  • нижняя

Любой объект сцены, находящийся вне этой области, не должен участвовать в рендеринге. Это базовый механизм оптимизации, позволяющий резко снизить количество отрисовываемых сущностей.

= {x ^3 }

В контексте CesiumJS frustum culling выполняет первичную фильтрацию объектов до стадии растеризации.


Геометрическая основа отсечения

Frustum задаётся матрицей проекции камеры:

M_{clip} = P V

где:

  • V — view matrix (матрица вида)
  • P — projection matrix (перспективная или ортографическая проекция)

Из этой матрицы извлекаются плоскости отсечения. CesiumJS строит их автоматически при каждом изменении камеры.

Каждая плоскость описывается уравнением:

ax + by + cz + d = 0

Точка считается видимой, если она находится по «правильную сторону» всех шести плоскостей.


Bounding volumes как основа ускорения

CesiumJS не проверяет каждый вершину объекта напрямую. Вместо этого используются ограничивающие объёмы:

  • сферы (BoundingSphere)
  • осесимметричные коробки (Axis-Aligned Bounding Box)
  • ориентированные коробки (Oriented Bounding Box)

Проверка сводится к тесту пересечения bounding volume с frustum.

Если объём полностью вне фрустума — объект отбрасывается.

Если пересекает — выполняется более детальная проверка или рендеринг.


Pipeline отсечения в CesiumJS

Внутренний pipeline включает несколько уровней:

  1. CPU frustum culling

    • выполняется до передачи в GPU
    • фильтрует примитивы и tiles
  2. Tile-based culling (3D Tiles)

    • каждый тайл имеет bounding volume
    • дерево плиток (quadtree/octree) позволяет быстро исключать большие области
  3. GPU clipping

    • финальная стадия в шейдерах
    • отсечение фрагментов вне frustum

Отсечение тайлов в 3D Tiles

Основной механизм оптимизации в CesiumJS связан с системой 3D Tiles.

Каждый тайл содержит:

  • boundingVolume
  • geometricError
  • children

Если bounding volume тайла не пересекает frustum, весь поддеревo исключается.

Это позволяет уменьшить сложность с:

O(n) O(n)

при обходе сцены.


Камера и динамическое обновление frustum

Frustum пересчитывается при каждом изменении камеры:

  • перемещение (translate)
  • вращение (rotate)
  • зум (zoom)

В CesiumJS это происходит через Scene и Camera:

const viewer = new Cesium.Viewer("cesiumContainer");

viewer.scene.preRender.addEventListener(() => {
    const frustum = viewer.camera.frustum;
});

Изменение параметров камеры напрямую влияет на плоскости отсечения, что вызывает перерасчёт видимости объектов.


Связь frustum culling и уровня детализации (LOD)

Frustum culling тесно связан с LOD-системой:

  • если объект вне frustum → исключается полностью
  • если внутри → выбирается уровень детализации
  • если пересекает границу → может загружаться частично

CesiumJS использует геометрическую ошибку (geometric error), чтобы определить необходимость подгрузки более детальных тайлов.


Проверка пересечения bounding sphere с frustum

Один из наиболее частых тестов — sphere-frustum intersection.

Алгоритм:

  • вычисляется расстояние от центра сферы до каждой плоскости
  • сравнивается с радиусом

Если расстояние меньше отрицательного радиуса — объект вне зоны видимости.


Оптимизация производительности через culling

Frustum culling влияет на:

  • количество draw calls
  • нагрузку на GPU
  • загрузку CPU при подготовке сцены

В сценах с глобусом без отсечения количество объектов может достигать миллионов, что делает рендеринг невозможным в реальном времени.

CesiumJS снижает нагрузку за счёт:

  • иерархического отсечения
  • кэширования bounding volumes
  • lazy loading тайлов

GPU-side clipping и фрагментное отсечение

После CPU-фильтрации включается GPU этап:

  • вершины, попавшие в clip space, проверяются аппаратно
  • фрагменты за пределами NDC отбрасываются

Clip space задаётся:

x_{ndc} =

Это финальный этап, который гарантирует корректное отображение геометрии даже при частичном пересечении frustum.


Практическая настройка и контроль culling

CesiumJS предоставляет инструменты для управления отсечением:

viewer.scene.globe.depthTestAgainstTerrain = true;
viewer.scene.globe.maximumScreenSpaceError = 2;

Также можно влиять на поведение камеры:

viewer.camera.frustum.fov = Cesium.Math.toRadians(60);
viewer.camera.frustum.near = 1.0;
viewer.camera.frustum.far = 10000000.0;

Изменение этих параметров влияет на объём frustum и, следовательно, на количество отсекаемых объектов.


Особенности culling на глобусе

В отличие от классических 3D-движков, CesiumJS работает с эллипсоидом Земли:

  • frustum пересекается с криволинейной поверхностью
  • bounding volumes часто аппроксимируются
  • важна корректная работа на горизонте

Из-за кривизны Земли многие объекты, формально вне прямого frustum, могут частично оставаться видимыми через геометрию поверхности.


Иерархическое отсечение сцен

Scene graph CesiumJS строится так, что каждый узел имеет bounding volume.

Алгоритм обхода:

  • проверка узла
  • если внутри frustum → обход детей
  • если вне → пропуск поддерева
  • если пересекает → частичная проверка

Это обеспечивает стабильную производительность даже при сложных сценах с 3D Tiles, моделями и terrain.