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

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

Оглавление

В предыдущей главе мы представили формат модели glTF и показали, как загрузить три простые, но красивые модели попугая, фламинго и аиста.

birds
birds

Эти модели были загружены из двоичных файлов glTF parrot.glb, flamingo.glb и stork.glb. Помимо моделей птиц, каждый из этих файлов также содержит анимационный клип летящей птицы.

В этой последней главе вводного раздела мы познакомим вас с системой анимации three.js и покажем, как прикрепить эти анимационные клипы к моделям птиц, чтобы они могли летать.

Система анимации three.js - это полноценный пульт для микширования анимации. Используя эту систему, вы можете анимировать практически любой аспект объекта, такой как положение, масштаб, поворот, цвет или непрозрачность материала, скелет меша, трансформирование целей и многое другое. Вы также можете смешивать и микшировать анимации, поэтому, например, если у вас есть анимация «ходьбы» и анимация «бег», прикрепленные к человеческому персонажу, вы можете заставить персонажа ускориться от прогулки к бегу, смешав эти анимации.

Система анимации использует ключевые кадры для определения анимации. Чтобы создать анимацию, мы устанавливаем ключевые кадры в определенные моменты времени, а затем система анимации заполняет за нас промежутки, используя процесс, известный как анимация движения. Например, чтобы анимировать прыгающий мяч, вы можете указать точки вверху и внизу отскока, и мяч будет плавно анимироваться во всех точках между ними. Количество необходимых ключевых кадров зависит от сложности анимации. Для очень простой анимации может потребоваться только один ключевой кадр в секунду или меньше, в то время как для сложной анимации потребуется больше, максимум до шестидесяти ключевых кадров в секунду (любое большее значение будет игнорироваться на стандартном дисплее с частотой 60 Гц).

Система анимации построена из ряда компонентов, которые работают вместе, чтобы создавать анимации, прикреплять их к объектам в сцене и управлять ими. Мы разделим их на две категории: создание анимации и воспроизведение и управление анимацией. Мы кратко представим здесь обе категории, а затем воспользуемся нашими новыми знаниями для настройки анимации полета, которую мы загрузили из трех файлов glTF.

Система анимации: создание анимации

Мы начнем с изучения того, как создать простую анимацию, которая изменяет видимость, масштаб или положение объекта. Однако следует отметить, что большинство людей не используют систему анимации three.js для создания анимации вручную. Он лучше всего подходит для использования с анимацией, созданной во внешнем программном обеспечении, таком как Blender. Вместо этого для создания анимации в коде большинство людей предпочитают использовать Tween.js для простых анимаций и GSAP для более сложных анимаций (хотя любая библиотека анимации JavaScript будет работать с three.js). Даже официальные примеры на сайте three.js используют Tween.js! Тем не менее, для нас важно понимать, как создаются и структурируются анимационные клипы, так что давайте начнем, и скоро эти ленивые птички появятся в небе!

В создании анимации участвуют три элемента: keyframes, KeyframeTrack и AnimationClip.

1. Keyframes

Самый нижний концептуальный уровень в системе анимации - это ключевой кадр. Каждый ключевой кадр состоит из трех частей информации: времени, свойства и значения, например:

  • В 0 секунд .position (0,0,0).
  • Через 3 секунды .scale(1,1,1).
  • Через 12 секунд material.color станет красным.

Каждый из этих трех ключевых кадров описывает значение некоторого свойства в определенное время. Однако ключевые кадры не определяют какой-либо конкретный объект. Ключевой кадр позиции может использоваться для анимации любого объекта со свойством .position, ключевой кадр масштаба может анимировать любой объект со свойством .scale и т. Д. Однако ключевые кадры указывают тип данных. Ключевые кадры .position и .scale выше определяют векторные данные, а ключевой кадр .material.color - данные цвета. В настоящее время система анимации поддерживает пять типов данных.

Подробнее в таблице

Примечательно, что в этом списке отсутствуют углы Эйлера, которые, если вы помните из нашей главы о преобразованиях, похожи на векторы и используются для хранения поворотов в Object3D.rotation. Для анимации вращения необходимо использовать Object3D.quaternion. Как мы уже упоминали в главе о преобразованиях, с кватернионами работать немного сложнее, чем с углами Эйлера, поэтому, чтобы не попасть в заблуждение, мы пока проигнорируем вращения и сосредоточимся на положении и масштабе.

Для создания анимации нам понадобится как минимум два ключевых кадра. Самый простой возможный пример - это два числовых ключевых кадра, например, анимация непрозрачности материала (насколько он непрозрачен / прозрачен):

  1. В 0 секунд .material.opacity равен 0.
  2. Через 3 секунды .material.opacity равна 1.

Нулевое значение непрозрачности означает полную невидимость, а значение непрозрачности - полностью видимое. Когда мы анимируем объект с использованием этих двух ключевых кадров, он исчезнет из поля зрения в течение трех секунд. Неважно, какова фактическая непрозрачность объекта, ключевые кадры переопределят это. Другими словами, если мы вручную установим:

mesh.material.opacity = 0.5;

… А затем анимируйте непрозрачность объекта, это значение 0,5 будет проигнорировано, и будет использоваться значение в ключевых кадрах. Возьмем другой пример. Вот три векторных ключевых кадра, представляющих позиции:

  1. В 0 секунд .position будет (0,0,0).
  2. Через 3 секунды .position будет (5,5,0).
  3. Через 6 секунд .position будет (0,0,0).

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

2. KeyframeTrack

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

Как и в случае с ключевыми кадрами, дорожки ключевых кадров не определяют какой-либо конкретный объект. Дорожка .material.opacity может анимировать любой объект с материалом, поддерживающим непрозрачность, дорожка .quaternion может анимировать любой объект со свойством quaternion и так далее.

KeyframeTrack - это базовый класс, и для каждого типа данных существует один подкласс:

Мы никогда не используем KeyframeTrack напрямую, вместо этого мы выберем подкласс, соответствующий анимируемому типу данных. Давайте посмотрим на пару примеров. Во-первых, мы будем использовать NumberKeyframeTrack для хранения этих пяти ключевых кадров .opacity:

  1. В 0 секунд .material.opacity равен 0.
  2. Через 1 секунду .material.opacity равна 1.
  3. Через 2 секунды .material.opacity равна 0.
  4. Через 3 секунды .material.opacityравна 1.
  5. Через 4 секунды .material.opacity равна 0.

Эти ключевые кадры заставят объект мигать в течение четырех секунд. Чтобы создать дорожку ключевых кадров, мы создадим один массив, содержащий время, и один массив, содержащий значения, а затем передадим их в конструктор NumberKeyframeTrack вместе со свойством, которое мы хотим анимировать.

import { NumberKeyframeTrack } from 'three';
const times = [0, 1, 2, 3, 4];
const values = [0, 1, 0, 1, 0];
const opacityKF = new NumberKeyframeTrack('.material.opacity', times, values);

Обратите внимание, как каждая запись в массиве times сопоставляется с одной записью в массиве значений. Затем давайте попробуем несколько ключевых кадров позиции и VectorKeyframeTrack:

  1. В 0 секунд .position (0,0,0).
  2. Через 3 секунды .position будет (2,2,2).
  3. Через 6 секунд .position будет (0,0,0).

Эти три ключевых кадра заставят объект начинать с центра сцены, перемещаться вправо, вверх и вперед в течение трех секунд, затем меняют направление и перемещают обратно в центр. Затем мы создадим векторную дорожку с этими ключевыми кадрами.

import { VectorKeyframeTrack } from 'three';
const times = [0, 3, 6];
const values = [0, 0, 0, 2, 2, 2, 0, 0, 0 ];
const positionKF = new VectorKeyframeTrack('.material.opacity', times, values);

На этот раз обратите внимание, как каждая запись в массиве times совпадает с тремя записями из массива values, представляющих позицию в трехмерном пространстве. Это означает, что массив values в три раза больше, чем массив times.

const times = [0, 3, 6];
const values = [
0, 0, 0, // (x, y, z) at t = 0
2, 2, 2, // (x, y, z) at t = 3
0, 0, 0 // (x, y, z) at t = 6
];

3. AnimationClip

Анимация танцующего персонажа, подобного изображенному в этой сцене, состоит из множества отдельных движений: вращение ступней, сгибание колен, резкое движение рук, голова кивает в такт (звуковая дорожка не прилагается). Каждое отдельное движение хранится в отдельной дорожке ключевых кадров, поэтому, например, одна дорожка управляет вращением левой ноги танцора, другая - вращением его правой ступни, третья - вращением его шеи и т. Д. . Фактически, эта танцевальная анимация состоит из пятидесяти трех треков ключевых кадров, из которых пятьдесят два являются треками .quaternion, управляющими отдельными суставами, такими как колени, локти и лодыжки танцора. Затем есть единственная дорожка положения, которая перемещает фигуру вперед и назад по полу.

FBXLoader

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

В анимационных клипах хранятся три части информации: имя клипа, длина клипа и, наконец, массив дорожек, составляющих клип. Если мы установим длину на -1, массив треков будет использоваться для вычисления длины (что вам и нужно в большинстве случаев). Давайте создадим клип, содержащий предыдущую дорожку с одной позицией:

import { AnimationClip, VectorKeyframeTrack } from 'three';
const times = [0, 3, 6];
const values = [0, 0, 0, 2, 2, 2, 0, 0, 0];
const positionKF = new VectorKeyframeTrack('.position', times, values);
// just one track for now
const tracks = [track];
// use -1 to automatically calculate
// the length from the array of tracks
const length = -1;
const clip = new AnimationClip('slowmove', length, tracks);

Поскольку мы установили длину на -1, треки будут использоваться для расчета длины, в данном случае шесть секунд. Мы дали клипу описательное имя, чтобы облегчить его использование в дальнейшем.

AnimationClip по-прежнему не прикреплен к какому-либо конкретному объекту. Для этого нам нужно дождаться AnimationAction ниже. Мы можем использовать этот простой клип, который мы создали, с любым объектом, имеющим свойство .position. Однако по мере того, как клипы становятся более сложными и содержат больше дорожек, они начинают становиться более привязанными к определенному объекту. Например, вы не можете использовать танцевальный клип с одной из загруженных птиц, поскольку они не имеют такой же внутренней структуры, как человеческая фигура. Однако вы можете использовать зажим с любой другой фигуркой гуманоида, имеющей такую ​​же внутреннюю структуру. Поскольку эта модель была загружена с сайта mixamo.com, танцевальный клип должен работать с другими персонажами с сайта mixamo.com, но вряд ли будет работать с любой загруженной вами моделью гуманоидов.

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

import {
AnimationClip,
NumberKeyframeTrack,
VectorKeyframeTrack,
} from 'three';
const positionKF = new VectorKeyframeTrack(
'.position',
[0, 3, 6],
[0, 0, 0, 2, 2, 2, 0, 0, 0],
);
const opacityKF = new NumberKeyframeTrack(
'.material.opacity',
[0, 1, 2, 3, 4, 5, 6],
[0, 1, 0, 1, 0, 1, 0],
);
const moveBlinkClip = new AnimationClip('move-n-blink', -1, [
positionKF,
opacityKF,
]);

Этот анимационный клип будет работать с любым объектом, имеющим свойство .position, а также материалом со свойством .opacity. Другими словами, он должен работать с сеткой. Он заставит сетку перемещаться, мигая. В очередной раз мы дали клипу запоминающееся название. Позже у нас может быть много отдельных клипов, и мы можем смешивать их вместе. Если дать каждому уникальное имя, это упростит нам задачу. На этот раз обратите внимание, что дорожка положения имеет три ключевых кадра, а дорожка непрозрачности - семь ключевых кадров. Кроме того, длина каждой дорожки одинакова. Это не обязательно, но анимация будет выглядеть лучше, если длины дорожек совпадают.

Продолжение в следующей части...