Один из организаторов митапов для креативных разработчиков в Бельгии Creative Front-end Belgium в двух постах на CodePen рассказывает и показывает, как шаг за шагом сделать туннель из частиц с эффектом движения в нём. К старту курса по Frontend-разработке делимся сокращённым переводом этих статей об анимации, которая, по словам автора, нравится ему больше всего; вы увидите эксперименты с параметрами анимации, поэтому легко поймёте, как адаптировать код для своих нужд, например, для эффекта на сайте или в вашей игре. Если есть что-то, что мне действительно нравится, то это туннельная анимация. Ниже расскажу, как сделать эту анимацию. Я добавил в демо ниже основу инициализации сцены Three.Js. Холст HTML (canvas). Немного CSS для приятного глазу отображения. Рендерер WebGL, сцена, камера и красный куб, чтобы удостовериться, что всё работает. Не забудьте добавить на страницу библиотеку Three.Js. Если вы видите куб, это значит, что можно продолжать: Чтобы создать трубу в Three.Js, сначала нам нужен его маршрут, для получения которого воспользуемся конструктором THREE.CatmullRomCurve3(), который позволит создать плавный сплайн из массива вершин. В этой демонстрации я жёстко закодировал массив точек, которые конвертирую в Vector3(). Со своим массивом вершин при помощи упомянутого конструктора вы можете создать свой маршрут: Получив маршрут, на его основе можно создать трубку туннеля: Сейчас вы должны видеть на сцене вращение трубы. В большинстве случаев жёстко кодировать маршрут не хочется. Можно написать функцию генерации случайного множества точек по какому-то также случайному алгоритму. Но в этой демо мы получаем значения из некоего SVG, созданного в Adobe Illustrator. Если вы не хотите кривую Безье, Illustrator экспортирует маршрут как полигон, например так: Из полигона мы можем вручную конвертировать SVG в массив: Если есть причина, вы можете создать функцию динамического конвертирования. Слева вы видите полигон. Труба следует за установленными нами точками. У нас есть труба, осталась основная часть — анимация. Воспользуемся полезной функцией маршрута — path.getPointAt(t). Эта функция возвращает точку конкретного процента прохождения маршрута. Этот процент — нормализованное значение от нуля до единицы. 0 — первая точка маршрута, 1 — последняя точка. Чтобы расположить камеру вдоль маршрута, применим функцию на каждом кадре: Функция getPointAt принимает только значения 0 и 1, нужно разделить проценты по модулю на единицу, чтобы гарантировать, что значение не выйдет за пределы 1. Это отлично сработает с позицией, но камера всегда смотрит в одном направлении. Сделаем так, чтобы камера также смотрела на точку вдоль маршрута, но немного дальше. На каждом кадре рассчитаем точку, где стоит камера, а также точку, на которую камера будет смотреть: Есть несколько вариантов обновления материала. Сейчас мы имеем простой материал, который сторонами смотрит наружу. И даже теперь, когда камера внутри трубы туннеля, нам нужно изменить стороны. И, поскольку на сцене нет освещения, чтобы легко видеть происходящее, мы можем изменить материал на каркасный: Вуаля! Камера в трубе перемещается. Не стану вдаваться в детали, но покажу, как поставить основное освещение в трубе. Принцип тот же, что и с перемещением камеры: чтобы правильно разместить освещение, будем полагаться на точку, куда смотрит камера. Сначала создадим PointLight (источник света) и добавим его на сцену: Меняем материал на чувствительный к свету: И, наконец, обновляем рендер функции, чтобы переместить свет: И вот результат: На последнем шаге я создал форк и проиграл анимацию с несколькими параметрами, чтобы создать новую. Посмотрите исходник, если интересно.  Для этой демо я установил разные цвета каждой грани. Так получилась мозаика: А здесь, чтобы сгенерировать маршрут, я играю с положением на оси Y. Таким образом труба изгибается не только в одной плоскости, а в трёх измерениях. А здесь я создал пять туннелей различного радиуса и цвета. Они также имеют разную прозрачность для хорошей видимости: Ниже я расскажу, как вместо простой геометрии TubeGeometry() сгенерировать частицы. Оригинал второй части. Чтобы достичь желаемого результата, сгенерируем круги частиц, все вдоль маршрута. Тот же подход используется Three.Js при генерации трубы, с той лишь разницей, что для создания поверхности трубы добавляются грани. Сначала нужно выбрать деталь трубы и её радиус. Деталь устанавливается двумя значениями: Теперь, когда мы более или менее знаем количество частиц (segments * circlesDetail), рассчитаем трёхгранники Френе. Я не эксперт в этой области, но, насколько понимаю, нам нужно рассчитать трёхгранники Френе для каждого сегмента трубы. Каждый трёхгранник состоит из касательной, нормали и бинормали. Приблизительно эти значения являются значениями поворота для каждого сегмента, а также значениями указывающими, куда смотрит камера. Если хочется лучше понимать расчёты, посмотрите статью Википедии. Благодаря Three.Js нам не нужно разбираться в том, как работает код, можно просто воспользоваться встроенной функцией объекта маршрута: Результат функции — набор из трёх массивов Vector3(): Теперь, имея всё необходимое для каждого сегмента, мы начнём генерировать частицы вдоль сегмента. Мы храним каждую точку частицы как Vector3 в Grometry() так, чтобы позже использовать точки повторно: Теперь частицы размещены в каждом сегменте. Вот для чего мы проходили циклом по всем сегментам. Я объясню, как работает функция. Посмотрите на код ниже, детали вы найдёте в комментариях: Понять этот код действительно непросто. Я сам смотрел на исходники Three.Js, чтобы написать его. Вы можете посмотреть демонстрацию ниже, чтобы увидеть, как частицы вычисляются одна за другой. Нажмите кнопку Rerun, если труба уже видна полностью. У нас есть заполненный вершинами объект Geometry. При помощи конструктора Points, позволяющего рендерить простые точки с прекрасной производительностью, вы можете создать приятные демонстрации с частицами, используя текстуры или различные цвета. Точно так, как при создании сетки Mesh, нам нужно создать два объекта Points; нужен материал и геометрия. Но геометрию мы уже определили на шаге 5, так что теперь поработаем над материалом: Чтобы всё перемещалось, повторно задействуем код из предыдущих демо: Простой туннель из частиц готов: Здесь я применил отдельный цвет для каждого вектора (Vector в коде), а также Fog на сцене, чтобы добиться эффекта затухания внутри туннеля: Этот туннель сделан из кубов. Вместо объекта Points в каждой позиции вершин я воспользовался объектом Mesh, а применяемые цвета основывались на алгоритме шума Перлина. Здесь я соединяю вершины каждой окружности, чтобы создать линии. Я играю с углом наклона вершин и их цветом, чтобы возникла иллюзия вращения: Из относительно простого, как будто плоского гипертекста веб превратился в пространство творчества, экспериментов, впечатлений. Если вы хотите посвятить себя веб-технологиям, то можете присмотреться к нашим курсам по фронтенду или Fullstack-разработке на Python, в рамках которых сможете поработать с HR, чтобы стать веб-разработчиком. Вы также можете узнать, как начать карьеру или вывести свои навыки на новый уровень в других направлениях: Python, веб-разработка Профессия Fullstack-разработчик на Python Курс «Python для веб-разработки» Профессия Frontend-разработчик Профессия Веб-разработчик Data Science и Machine Learning Профессия Data Scientist Профессия Data Analyst Курс «Математика для Data Science» Курс «Математика и Machine Learning для Data Science» Курс по Data Engineering Курс «Machine Learning и Deep Learning» Курс по Machine Learning Мобильная разработка Профессия iOS-разработчик Профессия Android-разработчик Java и C# Профессия Java-разработчик Профессия QA-инженер на JAVA Профессия C#-разработчик Профессия Разработчик игр на Unity От основ — в глубину Курс «Алгоритмы и структуры данных» Профессия C++ разработчик Профессия Этичный хакер А также: Курс по DevOps Редактор-переводчик