Three.js
March 21, 2022

Трансформация объектов (часть 2)

Перед тем как начать:

В предыдущем уроке мы начали разбираться с тем, как можно трансформировать объекты в three.js, научились перемещать их и изменять размер. Если ты это пропустил, то советую вернуться!

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

Вращение объектов

Вращение немного более сложный тип трансформации, относительно перемещения и масштабирования. Существует два способа реализовать вращение.

Можно использовать изменение вполне очевидного свойства rotation, но можно изменять и менее очевидное свойство quaternion. Three.js поддерживает оба варианта и изменение одного из этих свойств будет изменять другое.

Свойство rotation

Свойство rotation так же как и position или scale хранит внутри себя свойства x, y, z, но представлены эти свойства не от класса Vector3, а от класса Euler . Когда вы меняете параметры x, y, z объекта типа Euler , то вы можете представить, что проводите палку через центр вашего объекта вдоль оси, а потом начинаете вращать объект вокруг этой палки.

  • Если вращать по оси x, то это вращение похоже на вращение колеса машины
  • Если вращать по оси y, то это чем-то похоже на карусель
  • А если вращение по оси z, то это похоже на пропеллер на носу самолета

Значение переменных, отвечающих за вращение выражены в радианах. То есть, если вы хотите, чтобы объект совершил половину оборота, то изменить значение переменной нужно примерно на 3.1415969...

Я надеюсь вы помните что такое число π. В нативном JavaScript можно использовать приближенное значение этого числа Math.PI.

Сейчас давайте возьмем тот код, который у нас получился в конце прошлого урока его можно посмотреть тут , закомментируем часть, которая добавляет масштабирование и добавить вращение по двум осям:

mesh.rotation.x = Math.PI * 0.25;
mesh.rotation.y = Math.PI * 0.25;

В итоге должно получиться что-то вот такое:

Достаточно просто, верно? В целом да, но вот если мы начнем с вами комбинировать различные вращения, то можем получить очень неожиданные результаты. Почему? А все потому, что если вы, например, вращаете вокруг оси x, то и направление остальных осей тоже меняется.

Вращение применяется к осям в следующем порядке: x, y, z. Мы можем изменить этот порядок, используя функцию object.rotation.reorder('yxz').

Принцип работы класса Euler достаточно просто понять, но проблема с очередностью применения изменений может создать достаточно много неожиданного результата. Именно поэтому существует другое решение - класс Quaternion.

Свойство quaternion

Свойство quaternion - также отвечает за вращение, но с более тонким математическим подходом, который в итоге решает проблему очередности.

Мы не будем сильно углубляться в то, как работают кватернионы, в целом нужно знать две вещи:

  1. Под капотом кватернионов работает математика с чудесами матричных преобразований
  2. Изменяя свойство rotation - вы так же изменяете свойство quaternion

Ты посмотри на это!

Класс Object3D имеет замечательный метод под названием lookAt(...) , который может заставить объект посмотреть на что-то. Тут не нужна сложная математика, потому что объект просто повернем свою ось -z в направлении указанной цели.

Заметка

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

Параметр, который мы передаем в этот метод должен быть Vector3.

Давайте попробуем его использовать:

camera.lookAt(new THREE.Vector3(0, - 1, 0));

Кажется, что куб уехал наверх, но на самом деле это камера смотрит немного ниже куба.

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

camera.lookAt(mesh.position);

Комбинирование трансформаций

Вы можете объединять изменение position, rotation, quaternion, и scale в любом порядке. Результат будет одинаковый - это что-то вроде стейта объекта.

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

// Передвижение куба
mesh.position.x = -1;
mesh.position.y = -0.8;
mesh.position.z = 0.4;

// Масштабирование куба
mesh.scale.x = 0.5;
mesh.scale.y = 2;
mesh.scale.z = 0.7;

// Вращение куба
mesh.rotation.x = Math.PI * 0.25;
mesh.rotation.y = Math.PI * 0.25;

Группировка объектов

В какой-то момент вы, возможно, захотите сгруппировать объекты. Допустим, вы строите дом со стенами, дверями, окнами, крышей, кустами и т.д.

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

Хорошей альтернативой было бы сгруппировать все эти объекты в контейнер и масштабировать этот контейнер.

Это можно сделать при помощи класса Group.

Создайте объект класса Group и добавьте его на сцену. Дальше, когда вы будете создавать объекты, то вы можете добавлять их не на сцену, а в эту группу используя метод add(...).

Давайте закомментируем вызов метода lookAt(...) и код всех трансформаций, вместо нашего прошлого создадим три куба, объединим их в группу и применим трансформации к ней.

/**
 * Группировка обхектов
 */
const group = new THREE.Group();
group.scale.y = 0.7;
group.rotation.x = 0.5;
scene.add(group);
 
const cube1 = new THREE.Mesh(geometry, material);
cube1.position.x = - 1.2;
group.add(cube1);

const cube2 = new THREE.Mesh(geometry, material);
cube2.position.x = 0;
group.add(cube2);
 
const cube3 =  new THREE.Mesh(geometry, material);
cube3.position.x = 1.2;
group.add(cube3);

Ну что же, на этом сегодня все, тут вы можете найти код, который у нас получился, а дальше мы перейдем с вами к изучению анимации! Не забывайте подписываться на телеграм канал, чтобы не пропустить новые статьи и уроки, пока ✌️

Анимация в three.js ->