В Three.js привязка объектов к плоскостям и поверхностям является одной из ключевых техник для точного позиционирования 3D-объектов в сцене. Основная цель таких операций — определить координаты точки пересечения луча с поверхностью и использовать их для размещения или перемещения объектов.
Для работы с привязкой используется объект Raycaster. Он позволяет испускать луч из точки (обычно камеры или курсора мыши) в сцену и определять объекты, с которыми луч пересекается.
Пример создания луча:
const raycaster = new THREE.Raycaster();
const mouse = new THREE.Vector2();
// Обновление координат мыши в нормализованной системе координат
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
raycaster.setFromCamera(mouse, camera);
raycaster.intersectObjects(scene.children) возвращает
массив пересечений с объектами сцены. Каждый элемент массива
содержит:
point — координаты пересечения в мировых
координатах;object — объект, с которым произошло пересечение;face — информация о треугольной грани объекта.Плоскость в Three.js описывается через объект
THREE.Plane. Она задаётся нормалью и расстоянием до
начала координат. Для привязки объектов к плоскости используется метод
raycaster.ray.intersectPlane(plane, target), который
возвращает точку пересечения луча с плоскостью.
Пример:
const plane = new THREE.Plane(new THREE.Vector3(0, 1, 0), 0); // горизонтальная плоскость y = 0
const intersectionPoint = new THREE.Vector3();
if (raycaster.ray.intersectPlane(plane, intersectionPoint)) {
object.position.copy(intersectionPoint);
}
Здесь объект точно располагается на плоскости, независимо от угла камеры или направления луча.
Для сложных объектов, таких как неровные поверхности, используется
метод intersectObject или intersectObjects
Raycaster’а. Результат пересечения предоставляет точку и нормаль
поверхности в этой точке, что позволяет:
Пример привязки к мешу:
const intersects = raycaster.intersectObject(mesh, true);
if (intersects.length > 0) {
const intersectPoint = intersects[0].point;
const normal = intersects[0].face.normal.clone().transformDirection(mesh.matrixWorld);
object.position.copy(intersectPoint);
// Ориентация объекта по нормали поверхности
const quaternion = new THREE.Quaternion().setFromUnitVectors(
new THREE.Vector3(0, 1, 0), normal
);
object.quaternion.copy(quaternion);
}
Важно различать локальные и мировые
координаты. Пересечение луча возвращает точку в мировых
координатах, поэтому при работе с вложенными объектами или
трансформациями сцены необходимо учитывать преобразование
object.localToWorld() и
object.worldToLocal().
Для интерактивного перемещения объектов часто используют фиксацию по выбранным осям. Можно создавать вспомогательные плоскости, параллельные нужным осям, и пересекать с ними луч, чтобы объект двигался только по выбранной плоскости.
const dragPlane = new THREE.Plane(new THREE.Vector3(1, 0, 0), 0); // движение по плоскости XZ
При каждом обновлении положения объекта определяется пересечение луча с этой плоскостью и объект перемещается в точку пересечения, исключая смещение по нежелательной оси.
Для отладки и визуализации привязки полезно использовать THREE.PlaneHelper и THREE.GridHelper, чтобы видеть плоскости и сетку, по которым будет производиться привязка. Это упрощает понимание поведения Raycaster’а и точности размещения объектов.
Эти методы являются фундаментальными при реализации drag-and-drop, прикрепления объектов к сцене, генерации контента на поверхностях и интерактивного редактирования 3D-сцен в Three.js.