lookAt: направление объекта на точку

Метод lookAt является встроенным инструментом в Three.js для управления ориентацией объектов в 3D-пространстве. Он позволяет направить объект на заданную точку, автоматически вычисляя необходимый поворот вокруг осей. Используется преимущественно с объектами типа Mesh, Camera или любыми объектами, наследующимися от Object3D.

Синтаксис:

object.lookAt(x, y, z);

или

object.lookAt(vector);

где vector — экземпляр THREE.Vector3.

Ключевой момент: lookAt не перемещает объект. Он изменяет только ориентацию, поворачивая объект так, чтобы его локальная ось Z смотрела на указанную точку.


Принцип работы

Three.js использует правостороннюю систему координат. Локальная ось Z объекта считается направлением “вперед”. Метод lookAt строит матрицу поворота, выравнивая эту ось в направлении целевой точки. Важно помнить, что ось Y объекта в Three.js ориентирована вверх (up по умолчанию (0,1,0)), и метод будет учитывать это при вычислении вращения.

Пример базового использования:

const mesh = new THREE.Mesh(geometry, material);
mesh.position.set(0, 0, 0);
const target = new THREE.Vector3(10, 5, -7);
mesh.lookAt(target);

В этом примере объект mesh будет повернут так, чтобы его “лицо” (ось Z) было направлено на координаты (10, 5, -7).


Работа с камерой

Метод lookAt часто применяется для камеры:

const camera = new THREE.PerspectiveCamera(75, window.innerWidth/window.innerHeight, 0.1, 1000);
camera.position.set(0, 2, 5);
camera.lookAt(new THREE.Vector3(0, 0, 0));

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


Использование в анимации

Для динамических сцен lookAt может обновляться в каждом кадре. Например, при слежении за движущимся объектом:

function animate() {
    requestAnimationFrame(animate);
    
    movingSphere.position.x += 0.05;
    camera.lookAt(movingSphere.position);

    renderer.render(scene, camera);
}
animate();

В этом случае камера будет непрерывно следить за сферой, перемещающейся по сцене. Аналогично можно использовать lookAt для любого объекта, который должен “смотреть” на другой объект в реальном времени.


Особенности при работе с up

Свойство object.up определяет вектор “вверх” для объекта. По умолчанию (0,1,0). Изменение этого вектора позволяет создавать наклонные или необычные ориентации:

mesh.up.set(0, 0, 1);
mesh.lookAt(target);

Теперь объект будет выравниваться так, чтобы локальная ось Z смотрела на цель, а ось Z сцены будет восприниматься как “вверх” для объекта.


Проблемы с gimbal lock

При использовании lookAt могут возникать эффекты gimbal lock — потеря одной степени свободы вращения, если цель расположена точно по вертикали относительно объекта. Для обхода этого используется комбинация Quaternion и lookAt:

mesh.lookAt(target);
mesh.quaternion.normalize();

Использование кватернионов обеспечивает плавное и корректное вращение без артефактов, особенно при анимациях с поворотами на 360°.


Применение с группами объектов

lookAt может использоваться и на объектах типа THREE.Group. В этом случае все дочерние объекты группы поворачиваются вместе, сохраняя локальные координаты. Это удобно для сцен с несколькими элементами, которые должны совместно ориентироваться на цель:

const group = new THREE.Group();
group.add(mesh1, mesh2);
group.position.set(0,0,0);
group.lookAt(target);

Практические советы

  • Метод lookAt всегда ориентирует объект по локальной оси Z. Если требуется использовать другую ось в качестве “лица” объекта, необходимо предварительно повернуть геометрию.
  • Для камер и объектов с анимацией лучше обновлять lookAt в цикле рендеринга, чтобы сохранить синхронизацию с динамическими объектами.
  • Для сложных анимаций и плавных переходов рекомендуется использовать кватернионы, избегая прямого изменения rotation, так как это может вызвать некорректные повороты.

Закрепление знаний: пример комплексной сцены

const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, window.innerWidth/window.innerHeight, 0.1, 1000);
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);

const geometry = new THREE.BoxGeometry();
const material = new THREE.MeshStandardMaterial({ color: 0x00ff00 });
const cube = new THREE.Mesh(geometry, material);
scene.add(cube);

const light = new THREE.DirectionalLight(0xffffff, 1);
light.position.set(5, 10, 7.5);
scene.add(light);

camera.position.set(10, 5, 10);

function animate() {
    requestAnimationFrame(animate);
    cube.rotation.y += 0.01;

    // Камера следит за кубом
    camera.lookAt(cube.position);

    renderer.render(scene, camera);
}
animate();

В этом примере камера динамически следит за вращающимся кубом, демонстрируя комбинацию анимации и использования lookAt для реального 3D взаимодействия объектов.