Найти в Дзене
Иван Зотов

Система анимации three.js (Часть 2)

Оглавление
birds
birds

Система анимации: воспроизведение и управление

Теперь у нас есть простой анимационный клип, который заставляет объект двигаться, постепенно увеличиваясь и увеличиваясь. Следующий шаг - прикрепить этот клип к объекту, а затем воспроизвести его. Это подводит нас к двум последним компонентам системы анимации. Во-первых, AnimationMixer позволяет нам превратить статический объект в анимированный, и, наконец, AnimationAction соединяет клип с объектом и позволяет нам управлять им с помощью таких действий, как воспроизведение, пауза, цикл, сброс и т. д.

4. AnimationMixer

Чтобы анимировать объект, такой как сетка, с помощью системы анимации, мы должны подключить его к AnimationMixer. С этого момента мы будем называть AnimationMixer просто микшером. Нам нужен один микшер для каждого анимированного объекта в сцене. Микшер выполняет техническую работу, заставляя модель двигаться во времени анимационного клипа, будь то движение ступней, рук и бедер танцора или крыльев летающей птицы.

import { Mesh, AnimationMixer } from 'three';
// create a normal, static mesh
const mesh = new Mesh();
// turn it into an animated mesh by connecting it to a mixer
const mixer = new AnimationMixer(mesh);

Нам также нужно обновлять микшер каждый кадр, но мы вернемся к этому чуть позже.

5. AnimationAction

Последняя часть головоломки, AnimationAction, соединяет анимированный объект с анимационным клипом. В классе AnimationAction также расположены такие элементы управления, как пауза, воспроизведение, цикл и сброс. С этого момента мы будем сокращать AnimationAction до действия (это поможет, если вы будете выкрикивать «действие», как режиссер, когда создаете его). В отличие от других классов системы анимации, мы никогда не создаем действие напрямую. Вместо этого мы будем использовать AnimationMixer.clipAction, который обеспечивает кеширование действия микшером.

Давайте посмотрим на это в действии. Здесь мы берем moveBlinkClip, который мы создали несколько минут назад, затем подключаем сетку к микшеру и, наконец,. мы используем .clipAction вместе с клипом для создания действия.

import {
AnimationClip,
AnimationMixer,
} from 'three';
const moveBlinkClip = new AnimationClip('move-n-blink', -1, [
positionKF,
opacityKF,
]);
const mixer = new AnimationMixer(mesh);
const action = mixer.clipAction(moveBlinkClip);

Давайте посмотрим на другой пример. Предположим, у нас есть модель человека и клип идущего персонажа. Еще раз, мы подключаем модель к микшеру, а затем создаем действие, используя .clipAction. Затем мы немедленно устанавливаем состояние действия на воспроизведение:

const mixer = new AnimationMixer(humanModel);
const action = mixer.clipAction(walkClip);
// immediately set the animation to play
action.play();
// later, you can stop the action
action.stop();

Обратите внимание, что, хотя мы вызвали .play, анимация еще не запустится. Нам все еще нужно обновить микшер в цикле анимации, что мы сделаем сейчас.

Предположим, этот персонаж тоже может бегать и прыгать. Каждая анимация будет представлена ​​в отдельном клипе, и каждый клип должен быть связан с одним действием. Таким образом, точно так же, как между микшером и моделью существует взаимосвязь «один к одному», существует взаимосвязь «один к одному» между действием и анимационным клипом.

const mixer = new AnimationMixer(humanModel);
const walkAction = mixer.clipAction(walkClip);
const runnAction = mixer.clipAction(runClip);
const jumpAction = mixer.clipAction(jumpClip);

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

Еще одна вещь, которую вам нужно учитывать, - это то, что происходит, когда персонаж перестает идти и начинает бежать. Если вы мгновенно переходите от одной анимации к другой, это будет выглядеть не очень хорошо. К счастью, AnimationAction содержит элементы управления, которые позволяют смешивать два клипа, постепенно замедлять клип до остановки, зацикливать клип, воспроизводить в обратном направлении или с другой скоростью и многое другое. В начале главы мы утверждали, что система анимации three.js - это полноценный пульт для микширования анимации. Точнее, мы должны были сказать, что AnimationAction - это полноценный пульт для микширования анимации, поскольку именно здесь находится большинство элементов управления.

Skinning Blending

Обновите анимацию в цикле

Осталось сделать только одно, прежде чем анимация сможет воспроизводиться. Нам нужно обновить анимированный объект в цикле анимации. У микшера есть метод обновления, который принимает параметр временной дельты. Независимо от того, сколько времени мы потратим на Mixer.update, все действия, связанные с микшером, будут продвигаться вперед на это количество.

const mixer = new AnimationMixer(mesh);
const updateAmount = 1; // in seconds
mixer.update(updateAmount)

Однако обычно мы не хотим прыгать вперед ни на секунду. В каждом кадре мы хотим немного сдвинуть анимацию вперед, чтобы при рендеринге шестидесяти кадров в секунду мы видели плавную анимацию. Мы воспользуемся техникой, полученной нами несколько глав назад, когда мы впервые создали цикл анимации и использовали его для управления простым вращающимся кубом, поэтому вернитесь к Цикл анимации, чтобы освежиться. Короче говоря, мы измеряем, сколько времени требуется для рендеринга каждого кадра, сохраняем это в переменной с именем delta, а затем передаем это в метод обновления микшера.

const mixer = new AnimationMixer(mesh);
const clock = new Clock();
// you must do this every frame
const delta = clock.getDelta();
mixer.update(delta);

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

const mixer = new AnimationMixer(mesh);
mesh.tick = (delta) => mixer.update(delta);
updatables.push(mesh);

Это похоже на метод .tick элемента управления орбитой, который использовался несколько глав назад.

Воспроизведите анимационные клипы из Parrot.glb, Flamingo.glb и Stork.glb

Теперь, когда мы увидели, как создать очень простой, но несколько утомительный анимационный клип, который перемещает объект по сцене, постепенно увеличивая и уменьшая его, давайте обратим наше внимание на более интересные клипы, которые мы загрузили вместе с нашими тремя моделями птиц. Каждый из трех файлов, Parrot.glb, Flamingo.glb и Stork.glb, содержит как модель, так и анимационный клип этой модели в полете. Эти модели не сильно отличаются от простой кубической сетки, которую мы использовали в нескольких предыдущих главах. Каждая птица представляет собой единую сетку с геометрией и материалом, хотя геометрия имеет функцию, называемую целевыми объектами морфинга (также известные как смешанные формы). Цели морфинга позволяют нам определять две (или более) разные формы для одной геометрии. Здесь есть одна фигура с крыльями вверх и одна с крыльями вниз. Летающий клип анимируется между этими двумя формами, чтобы создать впечатление, будто крылья птицы хлопают.

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

  1. Найдите летающий клип из данных, загруженных из каждого файла glTF.
  2. Создайте AnimationMixer для управления каждой моделью птицы.
  3. Создайте AnimationAction, чтобы подключить клип к микшеру.
  4. Добавьте метод .tick к каждой птице и обновляйте микшер птицы каждый кадр.

Практически все можно сделать с помощью пары строк в файле birds / setupModel.js. В World нам нужно добавить птиц в массив обновляемой информации, чтобы анимация обновлялась в цикле.

Где найти загруженные анимационные клипы

В модуле components / birds / birds.js в настоящее время мы записываем необработанные данные, загруженные из Parrot.glb в консоль:

console.log('Squaaawk!', parrotData);

Откройте консоль браузера и посмотрите сейчас. Мы подробно описали эти данные в предыдущей главе, так что загляните туда, если вам нужно напомнить. Данные содержат два интересующих элемента: сетку в форме птицы, которую мы извлекли в предыдущей главе, и анимационный клип летящей птицы. В предыдущей главе мы нашли сетку в gltf.scene. Здесь мы извлечем анимационный клип и прикрепим его к сетке, чтобы птица взлетела. Вы найдете анимационный клип в массиве gltfData.animations:

{
animations: [AnimationClip]
asset: {…}
cameras: []
parser: GLTFParser {…}
scene: Scene {…}
scenes: […]
userData: {}
__proto__: Object
}

Здесь каждый файл содержит только один клип, но файл glTF может содержать любое количество анимационных клипов. Например, файл, содержащий модель человека, также может содержать отрывки, в которых персонаж идет, бежит, прыгает, сидит и т. д.

Затем обновите setupModels, чтобы извлечь клип:

function setupModel(data) {
const model = data.scene.children[0];
const clip = data.animations[0];
return model;
}

Создайте микшер и действие

Теперь мы создадим микшер и действие. Сначала импортируйте AnimationMixer. Мы будем использовать AnimationMixer.clipAction для создания действия, поэтому импортировать AnimationAction не нужно. Затем создайте миксер, передав модель птицы в конструктор.

import { AnimationMixer } from 'three';
function setupModel(data) {
const model = data.scene.children[0];
const clip = data.animations[0];
const mixer = new AnimationMixer(model);
return model;
}

Затем используйте .clipAction для создания действия, передав клип, а затем сразу установите действие на воспроизведение:

function setupModel(data) {
const model = data.scene.children[0];
const clip = data.animations[0];
const mixer = new AnimationMixer(model);
const action = mixer.clipAction(clip);
action.play();
return model;
}

Внутри этого метода мы вызываем mixer.update для каждого кадра, передавая дельту, которая представляет собой количество времени, которое потребовалось для отрисовки предыдущего кадра. Микшер использует дельту для синхронизации анимации даже при колебаниях частоты кадров. Снова вернитесь к главе 1.7 для более подробного обсуждения.

Добавить Птиц в updatables

Наконец, в World добавьте всех трех птиц в массив обновляемой информации:

async init() {
const { parrot, flamingo, stork } = await loadBirds();
// move the target to the center of the front bird
controls.target.copy(parrot.position);
loop.updatables.push(parrot, flamingo, stork);
scene.add(parrot, flamingo, stork);
}

На этом этапе, если все настроено правильно, ваши птицы полетят!

Вы дошли до конца первого раздела!

С нашими птицами на крыльях вы подошли к концу первого раздела. Поздравляю!

В этом разделе мы рассмотрели многое: камеры, геометрию, сетки, текстуры, физические материалы, прямое и окружающее освещение, рендеринг наших сцен с помощью WebGL, преобразования, системы координат и граф сцены, векторы, загрузку внешних моделей, формат ресурсов glTF и даже система анимации three.js, которая представляет собой сложную штуку. Узнав обо всем этом, мы также нашли время, чтобы создать простое, но хорошо структурированное приложение, которое вы можете использовать для приложений three.js любого размера.

Но не останавливайтесь сейчас! Этот раздел заложил основу, но нам еще предстоит пройти долгий путь, чтобы стать экспертами по three.js. В следующем разделе мы перейдем на новый уровень. Мы более подробно рассмотрим класс Color и познакомим вас с цветовыми пространствами, а затем покажем, как добавлять вспомогательные объекты в сцены.

Ссылка на статью в оригинале