Определение пересечений с объектами

В библиотеке Three.js определение пересечений с объектами играет ключевую роль в интерактивной 3D-графике. Основной инструмент для работы с пересечениями — это Raycaster. Он позволяет создавать лучи (rays), которые проверяют пересечения с объектами сцены и возвращают информацию о точках контакта.

const raycaster = new THREE.Raycaster();
const mouse = new THREE.Vector2();

Луч создается с позиции наблюдателя или курсора, и направляется в определённую точку в 3D-пространстве. Для вычисления направления луча с учётом положения мыши на экране используется метод setFromCamera.

mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;

raycaster.setFromCamera(mouse, camera);

После установки луча вызывается метод intersectObjects, который принимает массив объектов для проверки и возвращает массив объектов пересечения, отсортированных по расстоянию от начала луча.

const intersects = raycaster.intersectObjects(scene.children, true);
intersects.forEach(intersect => {
    console.log(intersect.object.name, intersect.point);
});

Параметр true указывает на рекурсивную проверку всех дочерних объектов, что особенно важно для сложных иерархий объектов.


Структура объекта пересечения

Каждый элемент массива пересечения содержит несколько ключевых полей:

  • object — объект Three.js, с которым произошло пересечение.
  • point — точка в пространстве, где произошло пересечение.
  • face — данные о пересечённой грани (face) объекта, включая индексы вершин.
  • faceIndex — индекс пересечённой грани.
  • distance — расстояние от начала луча до точки пересечения.
  • uv — координаты текстуры в точке пересечения (если применимо).

Эта информация позволяет реализовать как простое выделение объектов, так и сложное взаимодействие, например, выбор точек на модели или физическое взаимодействие объектов.


Лучи и координатные системы

Для корректного расчёта пересечений необходимо учитывать различие между координатами экрана и координатами мира:

  • Координаты мыши преобразуются в NDC (Normalized Device Coordinates) — диапазон от -1 до 1.
  • Луч формируется с позиции камеры и направлен в точку в мировых координатах.
  • В случае движения камеры или объекта пересчёт луча должен выполняться каждый кадр, чтобы сохранять актуальность пересечений.
function onMouseMove(event) {
    mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
    mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
}
window.addEventListener('mousemove', onMouseMove, false);

Интерактивные сцены и выделение объектов

Частая задача — визуальное выделение объекта при наведении. Для этого используется массив пересечений:

function highlightObject() {
    const intersects = raycaster.intersectObjects(scene.children, true);
    if (intersects.length > 0) {
        intersects[0].object.material.emissive.set(0xff0000);
    }
}

При необходимости сброса выделения требуется хранить предыдущий объект и возвращать его материал к исходному состоянию.


Оптимизация проверки пересечений

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

  • Фильтрацию объектов, проверяя только те, которые реально могут пересечься с лучом.
  • Иерархические структуры, такие как THREE.Group, для рекурсивной проверки только видимых ветвей сцены.
  • Bounding volumes (оболочки), например BoundingBox или BoundingSphere, для предварительной проверки пересечения перед детальной проверкой геометрии.
object.geometry.computeBoundingBox();
if (raycaster.ray.intersectsBox(object.geometry.boundingBox)) {
    // Проверка пересечения с точной геометрией
}

Пересечения с нестандартными объектами

Для локационных эффектов и нестандартных форм можно использовать кастомные объекты:

  • Линии (THREE.Line): метод intersectObject возвращает пересечение с ближайшей точкой линии.
  • Плоскости (THREE.Plane): можно использовать ray.intersectPlane(plane) для вычисления пересечения вручную.
  • Трёхмерные модели с несколькими материалами: важно учитывать, что intersectObjects возвращает объект пересечения для каждой грани с материалом.

Применение пересечений в анимации

Пересечения активно используются для:

  • Выбора объектов и интерфейсов в 3D-пространстве.
  • Систем частиц для столкновений с поверхностями.
  • Физики и движения персонажей, когда требуется проверка линии видимости или трассировка пули.
  • Ray picking в редакторах сцен для перемещения, вращения и масштабирования объектов.

В анимационных циклах пересчёт пересечений должен выполняться после обновления позиции камеры и объектов:

function animate() {
    requestAnimationFrame(animate);
    raycaster.setFromCamera(mouse, camera);
    highlightObject();
    renderer.render(scene, camera);
}
animate();

Использование Raycaster в Three.js обеспечивает точное и эффективное определение пересечений, что является фундаментом для интерактивной и динамической 3D-графики. Контроль координат, рекурсивная проверка и оптимизация для больших сцен позволяют создавать сложные визуальные эффекты и интерактивные системы с высокой производительностью.