Frustum culling: автоматическое и ручное

Frustum culling — это процесс отбраковки объектов сцены, которые находятся вне видимого объёма камеры (frustum), чтобы снизить нагрузку на GPU и увеличить производительность. В Three.js фрустум задаётся с помощью перспективной или ортографической камеры и представляет собой усечённую пирамиду, ограничивающую видимую область сцены.


Автоматическое frustum culling

Three.js по умолчанию выполняет автоматическое отсечение объектов, используя свойство frustumCulled объектов THREE.Object3D.

const mesh = new THREE.Mesh(geometry, material);
mesh.frustumCulled = true; // Включено по умолчанию
scene.add(mesh);

Механизм работы:

  1. Для каждого рендеринга Three.js вычисляет фрустум камеры.
  2. Проверяет bounding sphere объекта (geometry.boundingSphere) на пересечение с фрустумом.
  3. Если объект полностью вне фрустума, он пропускается и не передаётся в GPU для рендеринга.

Важно: для корректного отсечения геометрия должна иметь рассчитанную границу:

geometry.computeBoundingSphere();

Без этого Three.js может некорректно определять видимость объекта.


Bounding volumes

Three.js использует простые геометрические объёмы для быстрой проверки:

  • BoundingSphere — сферическая граница объекта. Самая быстрая проверка.
  • BoundingBox (Box3) — прямоугольная рамка по осям XYZ. Более точная, но требует ручной проверки.
geometry.computeBoundingBox();
const boundingBox = geometry.boundingBox;

Для сложных объектов можно комбинировать bounding sphere и bounding box для оптимизации.


Ручное frustum culling

Ручное отсечение позволяет контролировать видимость объектов вне автоматического процесса. Для этого используется класс THREE.Frustum и матрица камеры camera.projectionMatrix вместе с camera.matrixWorldInverse.

Пример:

const frustum = new THREE.Frustum();
const cameraViewProjectionMatrix = new THREE.Matrix4();

camera.updateMatrixWorld(); // Обновление матрицы мира камеры
cameraViewProjectionMatrix.multiplyMatrices(
    camera.projectionMatrix,
    camera.matrixWorldInverse
);
frustum.setFromProjectionMatrix(cameraViewProjectionMatrix);

scene.traverse((object) => {
    if (object.isMesh) {
        object.visible = frustum.intersectsObject(object);
    }
});

Пояснения:

  • camera.matrixWorldInverse — обратная матрица мира камеры.
  • frustum.setFromProjectionMatrix создаёт фрустум для проверки видимости.
  • frustum.intersectsObject(object) проверяет, пересекается ли объект с фрустумом. Возвращает true или false.
  • Свойство object.visible управляет рендерингом вручную.

Оптимизация culling для динамических сцен

  1. Обновление bounding volumes: Если объект изменяет форму или позицию, нужно пересчитывать boundingSphere или boundingBox.
geometry.computeBoundingSphere(); // после трансформаций
  1. Группировка объектов: Объекты, движущиеся вместе, можно объединять в THREE.Group и отсекать сразу всю группу, снижая количество проверок.

  2. Использование LOD (Level of Detail): Для дальних объектов применять простые модели или отключать их полностью через culling.


Взаимодействие с другими механизмами оптимизации

  • InstancedMesh: при рендеринге большого количества идентичных объектов автоматическое culling работает для всей инстанции. Для индивидуального контроля нужна ручная проверка каждой инстанции.
  • Octree / BVH: структуры пространственного разделения могут ускорять ручное culling, проверяя фрустум только для узлов с объектами.
  • Frustum culling vs Occlusion culling: Three.js автоматически не выполняет occlusion culling (проверка перекрытия объектов). Для сложных сцен нужно использовать дополнительные алгоритмы.

Практические рекомендации

  • Всегда включать frustumCulled = true для объектов, не требующих полного контроля.
  • Для крупных и динамичных сцен использовать ручное culling с THREE.Frustum.
  • Обновлять bounding volumes после значительных трансформаций или изменений геометрии.
  • Комбинировать culling с LOD и структурой сцен (группы, octree) для максимальной производительности.

Ключевые методы и свойства

Метод / Свойство Назначение
object.frustumCulled Включает/выключает автоматическое отсечение объекта
geometry.computeBoundingSphere() Вычисляет сферу для culling
geometry.computeBoundingBox() Вычисляет bounding box для ручного отсечения
THREE.Frustum() Создаёт объект фрустума для проверки видимости
frustum.setFromProjectionMatrix(matrix) Устанавливает фрустум из матрицы камеры
frustum.intersectsObject(object) Проверяет, пересекается ли объект с фрустумом
object.visible Управляет видимостью объекта при ручном culling

Автоматическое и ручное frustum culling в Three.js позволяет эффективно управлять рендерингом, снижать нагрузку на графический процессор и поддерживать высокую частоту кадров даже в сложных сценах с большим количеством объектов.