9 мин.

Анимация туннеля - Часть 1

Анимация туннеля - Часть 1

Привет, читатель! 👋
Есть одна вещь, которую я действительно люблю, это туннельную анимацию 😍. Не понимаешь о чём? Тогда посмотри мои демки, которые я делал раньше:

Я даже использовал этот тип анимации для своей поздравительной открытки 2017 года.

Я попытаюсь в этом посте раскрыть основные моменты создания таких демок. Первым шагом нам надо создать трубу и анимацию перемещения в ней. А далее уже будем изменять внешний вид трубы и как то её приводить в божеский вид.

Для это демо я собираюсь использовать Three.js для всего, что относится к WebGL. Если вы понимаете, что ничего не понимаете в WebGL и Three.js частности, то вы можете прочитать пост Rachel Smith об этом. (На английском)

{{% toc %}}

{{% /toc %}}

Настройка сцены

Для начала я добавлю в моё демо все, что нужно для инициализации сцены Three.js

  • canvas элемент в HTML
  • Немного CSS для лучшего вида
  • Рендеринга WebGL'ом сцены, камеры и красного куба, чтобы быть уверенным, что все работает нормально.

Не забудьте подключить Three.js библиотеку в вашу демку/страницу.

See the Pen Setup the scene by Louis Hoebregts (@Mamboleoo) on CodePen.

Если вы видите красный вращающийся куб, это значит что все отлично и можно продолжать 📦 !

Создание геометрии трубы

Чтобы создать трубу в Three.js нам надо в начале создать путь её следования. Для этого будем использовать THREE.CatmullRomCurve3() конструктор. Он позволяет нам создать сглаженную линию из массива вершин. Для этого демо, я просто создам массив, который еонвернирую в Vector3().

Когда у нас есть наш массив вершин, мы можем создать путь с помощью конструктора функции.

// Жестко закодированный массив точек
var points = [
  [0, 2],
  [2, 10],
  [-1, 15],
  [-3, 20],
  [0, 25],
];

// Переводим массив точек в вершины
for (var i = 0; i < points.length; i++) {
  var x = points[i][0];
  var y = 0;
  var z = points[i][1];
  points[i] = new THREE.Vector3(x, y, z);
}
// Создание пути из вершин
var path = new THREE.CatmullRomCurve3(points);

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

// Создание геометрии трубы из пути
// 1й параметр это сам путь
// 2й параметр это кол-во сегментов, из которых будет сделана труба
// 3й параметр это радиус трубы
// 4й параметр это кол-во сегментов по радиусу
// 5й параметр это специфический памеремт, если мы хотим закрытую трубу или нет
var geometry = new THREE.TubeGeometry(path, 64, 2, 8, false);
// Добавим материал с красным цветом
var material = new THREE.MeshBasicMaterial({ color: 0xff0000 });
// Создание меша
var tube = new THREE.Mesh(geometry, material);
// Добавляем трубу на сцену
scene.add(tube);

See the Pen Create a tube geometry by Louis Hoebregts (@Mamboleoo) on CodePen.

Теперь мы можем видеть красную, вращающуюся трубу на сцене 😊.

Создание трубы из SVG полигонов

В большинстве случаев нам не надо просто записывать точки пути на наш код. Вы можете написать функцию генерации случайного набора точек с помощью одного из алгоритмов. Но в нашем случае мы можем взять значения из SVG, который мы создадим в одном из векторных редакторов, например в Adobe Illustrator.
Если в не выберите кривые безье на созданной кривой, то Illustrator экспортирует ваш путь в виде многоугольников следующим образом:

<svg viewBox="0 0 346.4 282.4">
  <polygon
    points="68.5,185.5 1,262.5 270.9,281.9 345.5,212.8 178,155.7 240.3,72.3 153.4,0.6 52.6,53.3 "
  />
</svg>

Этот многоугольник вы можете преобразовать вручную в массив:

var points = [
  [68.5, 185.5],
  [1, 262.5],
  [270.9, 281.9],
  [345.5, 212.8],
  [178, 155.7],
  [240.3, 72.3],
  [153.4, 0.6],
  [52.6, 53.3],
  [68.5, 185.5],
];
// Не забудьте установить последний параметр как True, чтобы труба была замкнутой
var geometry = new THREE.TubeGeometry(path, 300, 2, 20, true);

Так же, если вы достаточно ленивы для того, чтобы каждый раз брать вручную набор координат, мы можете создать функцию для автоматического конвертации SVG строки в массив 😉

See the Pen Create a tube from a SVG polygon by Louis Hoebregts (@Mamboleoo) on CodePen.

Перемещение камеры внутри трубы

Теперь у нас есть труба, нам осталась самая важная часть - анимация!
Мы будем использовать полезную функцию нашей траектории path.getPointAt(t). Эта функция возвращает координаты на нашей траектории в зависимости от процента. Процент (t) - это нормализованное значение от 0 до 1. Нулевое значение это начало пути, а Единица это последняя точка нашего пути.

Мы используем функию, которая на каждом кадре будет перемещать камеру внутри. Нужно ещё увеличивать t каждый кадр, чтобы магия сработала.

// Начинаем с 0 процентов
var percentage = 0;
function render() {
  // Увеличиваем процент
  percentage += 0.001;
  // Получаем координату в  зависимости от нашего процента
  var p1 = path.getPointAt(percentage % 1);
  // Устанавливаем камеру в эти координаты
  camera.position.set(p1.x, p1.y, p1.z);

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

Поскольку .getPointAt() принимает значение только от 0 до 1, то нам надо взять остаток от деления на единицу, чтобы он не когда становился больше единицы.

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

var percentage = 0;
function render() {
  percentage += 0.001;
  var p1 = path.getPointAt(percentage % 1);
  // Получить другую точку на нашем пути, но чуть дальше
  var p2 = path.getPointAt((percentage + 0.01) % 1);
  camera.position.set(p1.x, p1.y, p1.z);
  // Повернуть камеру в направлении второй точки
  camera.lookAt(p2);

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

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

var material = new THREE.MeshBasicMaterial({
  color: 0xff0000, // Красный цвет
  side: THREE.BackSide, // Развернуть стенки внутрь
  wireframe: true, // Отображать трубу как сетку
});

И вуаля, наша камера летит внутри трубы! 🎉

See the Pen Move the camera inside the tube by Louis Hoebregts (@Mamboleoo) on CodePen.

Добавляем освещение

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

  • Для начале создадим PointLight и добавим его на сцену 💡.
// Создание точечного источника освещения на нашей сцене
var light = new THREE.PointLight(0xffffff, 1, 50);
scene.add(light);
  • Затем мы уберём в нашём материале параметр показа сетки, чтобы освещение начало работать.
var material = new THREE.MeshLambertMaterial({
  color: 0xff0000,
  side: THREE.BackSide,
});
  • И наконец мы обновим функцию рендринга, для перемещения нашего источника освещения.
var percentage = 0;
function render() {
  percentage += 0.0003;
  var p1 = path.getPointAt(percentage % 1);
  var p2 = path.getPointAt((percentage + 0.02) % 1);
  camera.position.set(p1.x, p1.y, p1.z);
  camera.lookAt(p2);
  light.position.set(p2.x, p2.y, p2.z);

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

Наконец результат !

See the Pen Add a light by Louis Hoebregts (@Mamboleoo) on CodePen.

Давайте оторвемся

Основываясь на последнем шаге, я форкну его и поиграюсь с некоторыми параметрами, для создания разных видов анимации. Посмотрите исходники, если вам интересно 😉

  • Для этого примера, я установил случайный цвет для каждой грани. Получился веселый мозаичный узор.

See the Pen Crazy 1 by Louis Hoebregts (@Mamboleoo) on CodePen.

  • В этом случае я играюсь с Y позицией точек генерации траектории. Таким образом траектория изменяется не в плоскости, а в пространстве.

See the Pen Crazy 2 by Louis Hoebregts (@Mamboleoo) on CodePen.

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

See the Pen Crazy 3 by Louis Hoebregts (@Mamboleoo) on CodePen.


Вот и закончилась первая часть. В следующем посте я объясню как создать туннель из частиц, без использования TubeGeometry() из ThreeJS. Вы можете найти все демки из поста в этой коллекции.

Я надеюсь вы что-то почерпнули из этого поста! Если у вас остались вопросы, задавайте их в комментариях.


https://codepen.io/Mamboleoo/post/tunnel-animation-1


https://github.com/grishy/blog/blob/hugo/content/post/tunnel-animation-1.md