3D-анимация частиц для Three.js: с китайским Новым годом

внешний интерфейс JavaScript three.js
3D-анимация частиц для Three.js: с китайским Новым годом

PK Creative празднует Китайский Новый год. Я участвую в "Творческом конкурсе Весеннего фестиваля". Подробности см.:Творческий конкурс "Праздник весны"

Я снял звезды с неба и хотел послать тебе благословение на праздник Весны.

Вы можете часто слышать слово «анимация частиц», так что же такое анимация частиц?

Частицы — это мельчайшие единицы, из которых состоят такие объекты, как атомы и молекулы. В 2D эта наименьшая единица — пиксель, а в 3D — вершина.

Анимация частиц относится не к анимации самого объекта, а к анимации этих основных единиц. Поскольку это анимация единиц, составляющих объект, она будет иметь эффект разрушения и реорганизации.

В этой статье мы узнаем об анимации 3D-частиц и создадим эффект благословения звезд:

Анализ мыслей

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

То есть,3D-модель — это геометрия (Geometry), определяемая вершинами, и объект (Mesh и т. д.), состоящий из различных текстур (Material).

Позже,Добавьте 3D-объекты в сцену (Scene), установите угол камеры (Camera) для наблюдения, а затем используйте средство визуализации (Renderer) для покадрового рендеринга, это процесс 3D-рендеринга.

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

В эффекте "Благословение звездами" мы разбиваем звезды и собираем их заново в персонажа благословения.По сути, вершина звезды перемещается в вершину персонажа благословения, и 3D-объект становится другим 3D-объектом. .

Так откуда же взялись вершины звезд? Откуда взялась вершина слова удача?

Вершины звезд на самом деле являются случайно сгенерированными точками в разных позициях.Вставьте текстуру звезд на эти точки, что является эффектом звезд.

Вершины персонажа Фу представляют собой загруженную 3D-модель, полученную путем анализа данных вершин.

С данными вершин двух 3D-объектов, то есть координатами начала и конца анимации, анимация частиц может быть достигнута путем непрерывного изменения свойств x, y и z каждой вершины.

Не рассчитывайте изменения значений атрибутов x, y и z самостоятельно. Используйте для их расчета некоторые библиотеки анимации. Они поддерживают функции времени, такие как ускорение и замедление. Библиотека анимации для Three.js называется Tween.js.

Вкратце,Анимация 3D-частиц — это изменение свойств x, y и z вершин, а библиотека анимации используется для вычисления промежуточных значений свойств. От положения вершины одного объекта до положения вершины другого объекта будет своего рода эффект разрыва и реорганизации, что также является прелестью анимации частиц.

Идея ясна, тогда давайте напишем код подробно.

Код

Как упоминалось ранее, для 3D-рендеринга требуется сцена (Scene) для управления всеми 3D-объектами, камера (Camera) для наблюдения под разными углами и модуль рендеринга (Renderer) для покадрового рендеринга.

Эта часть является базовым кодом, сначала напишите эту часть:

Создайте сцену:

const scene = new THREE.Scene();

Создайте камеру:

const width = window.innerWidth;
const height = window.innerHeight;
const camera = new THREE.PerspectiveCamera(45, width / height, 0.1, 1000);

Камеры делятся на перспективные камеры и параллельные камеры.Перспективная камера, которую мы здесь используем, представляет собой перспективный эффект ближнего, большого и дальнего. Указать три параметра: видимое поле зрения, угол обзора (45°), соотношение сторон (ширина/высота) и расстояние (от 0,1 до 1000).

Отрегулируйте положение камеры и направление просмотра:

camera.position.set(100, 0, 400);
camera.lookAt(scene.position);

И затем рендерер:

const renderer = new THREE.WebGLRenderer();
renderer.setSize(width, height);
document.body.appendChild(renderer.domElement);

Рендерер должен рендерить кадр за кадром через requestAnimationFrame:

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

Подготовка завершена, и следующим шагом будет отрисовка двух 3D-объектов, звездного неба и благословляющего персонажа, а также реализация анимации частиц.

нарисовать звездное небо

Звездное небо не является регулярной геометрией, такой как куб или цилиндр, а состоит из некоторых случайных вершин Эта произвольная геометрия создается с помощью буферной геометрии BufferGeometry.

Почему эта геометрия состоит из произвольных вершин и называется буферной геометрией?

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

Создаем 30000 случайных вершин:

const vertices = [];
for ( let i = 0; i < 30000; i ++ ) {
    const x = THREE.MathUtils.randFloatSpread( 2000 );
    const y = THREE.MathUtils.randFloatSpread( 2000 );
    const z = THREE.MathUtils.randFloatSpread( 2000 );
    vertices.push( x, y, z );
}

Здесь инструмент MathUtils, предоставленный Three.js, используется для генерации случайных значений от 0 до 2000.

Затем создайте BufferGeometry с этими вершинами:

const geometry = new THREE.BufferGeometry();
geometry.setAttribute( 'position', new THREE.Float32BufferAttribute(vertices, 3));

Задайте положение вершины для объекта BufferGeometry, указав 3 значения (x, y, z) в качестве координаты.

Затем создайте материал на этих вершинах, который является картой звезд:

const star = new THREE.TextureLoader().load('img/star.png');
const material = new THREE.PointsMaterial( { size: 10, map: star });

С помощью вершин и материалов можно создавать 3D-объекты (3D-объекты здесь — это точки).

const points = new THREE.Points( geometry, material );
scene.add(points);

Взгляните на эффект рендеринга:

Статика без ощущения 3D, давайте повернем каждый кадр и изменим логику рендеринга:

function render() {
    renderer.render(scene, camera);
    scene.rotation.y += 0.001;

    requestAnimationFrame(render);
}

Посмотри снова:

Ощущение 3D звездного неба есть!

Далее займемся анимацией частиц:

3D анимация частиц

Трехмерная анимация частиц — это анимация вершин, то есть изменений x, y и z.

Давайте сначала реализуем самый простой эффект, пусть звезды переместятся в положение 0, 0, 0:

Координаты начальной точки — это исходные положения звезд, полученные с помощью getAttribute('position'). Процесс анимации рассчитывается с помощью tween.js:

const startPositions = geometry.getAttribute('position');

for(let i = 0; i< startPositions.count; i++) {
    const tween = new TWEEN.Tween(positions);

    tween.to({
        [i * 3]: 0,
        [i * 3 + 1]: 0,
        [i * 3 + 2]: 0
    }, 3000 * Math.random());

    tween.easing(TWEEN.Easing.Exponential.In);
    tween.delay(3000);
    tween.onUpdate(() => {
        startPositions.needsUpdate = true;
    });
    
    tween.start();
}

Каждая точка имеет координаты x, y, z, то есть индекс i3. яЗначением 3+1, i*3+2, мы задаем движение от начального положения звезд к положению 0, 0, 0.

Затем укажите функцию времени как ускорение (Easing.Exponential.In) и запустите анимацию через 3000 мс.

Tween.update вызывается для вычисления последнего значения при рендеринге каждого кадра:

function render() {
    TWEEN.update();
    renderer.render(scene, camera);
    scene.rotation.y += 0.001;

    requestAnimationFrame(render);
}

Функция обратного вызова onUpdate вызывается при отрисовке каждого кадра.Мы устанавливаем для needUpdate позиций значение true в функции обратного вызова, что означает, что tween.js обновляется до нового значения в этом кадре перед рендерингом.

Первая анимация частиц готова!

Давайте посмотрим на эффект (я называю этот эффект Вьентьян Тяньинь):

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

Затем, пока частицы перемещаются к вершине символа благословения, мы собираемся выполнить эффект «звезды посылают благословения».

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

Я нашел 3D-модель такого благословения:

Модель в формате fbx и загружается с помощью FBXLoader:

const loader = new THREE.FBXLoader();
loader.load('./obj/fu.fbx', function (object) {
    const destPosition = object.children[0].geometry.getAttribute('position');

});

Параметр обратного вызова — это 3D-объект, загруженный из fbx-модели, которая представляет собой Группу (набор нескольких 3D-объектов), и извлекается атрибут геометрии 0-го элемента, который является соответствующей геометрией.

Таким образом, мы получаем позицию вершины цели.

Просто измените конечную позицию анимации частиц на вершину персонажа Фу:

const cur = i % destPosition.count;
tween.to({
    [i * 3]: destPosition.array[cur * 3],
    [i * 3 + 1]: destPosition.array[(cur * 3 + 1)],
    [i * 3 + 2]: destPosition.array[(cur * 3 + 2)]
}, 3000 * Math.random());

Если начальных позиций вершин много, лишняя часть вернется из положения 0, поэтому возьмите остаток.

Готово!

Это эффект частиц, который нам нужен:

Полный код загружен на github:GitHub.com/кварк глюон P…

Также размещаю копию здесь:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title></title>
    <style>
        body {
            margin: 0;
        }
    </style>
    <script src="./js/three.js"></script>
    <script src="./js/tween.js"></script>
    <script src="./js/FontLoader.js"></script>
    <script src="./js/TextGeometry.js"></script>
    <script src="./js/FBXLoader.js"></script>
    <script src="./js/fflate.js"></script>
</head>
<body>
    <script>
        const width = window.innerWidth;
        const height = window.innerHeight;
        const camera = new THREE.PerspectiveCamera(45, width / height, 0.1, 1000);

        const scene = new THREE.Scene();
        const renderer = new THREE.WebGLRenderer();

        camera.position.set(100, 0, 400);
        camera.lookAt(scene.position);

        renderer.setSize(width, height);
        document.body.appendChild(renderer.domElement)

        function create() {
            const vertices = [];
            for ( let i = 0; i < 30000; i ++ ) {
                const x = THREE.MathUtils.randFloatSpread( 2000 );
                const y = THREE.MathUtils.randFloatSpread( 2000 );
                const z = THREE.MathUtils.randFloatSpread( 2000 );
                vertices.push( x, y, z );
            }
            const geometry = new THREE.BufferGeometry();
            geometry.setAttribute( 'position', new THREE.Float32BufferAttribute( vertices, 3 ) );

            const star = new THREE.TextureLoader().load('img/star.png');
            const material = new THREE.PointsMaterial( { size: 10, map: star });

            const points = new THREE.Points( geometry, material );
            points.translateY(-100);
            scene.add(points);

            const loader = new THREE.FBXLoader();
            loader.load('./obj/fu.fbx', function (object) {
                const startPositions = geometry.getAttribute('position');
                const destPosition = object.children[0].geometry.getAttribute('position')
                for(let i = 0; i< startPositions.count; i++) {
                    const tween = new TWEEN.Tween(startPositions.array);
                    const cur = i % destPosition.count;
                    tween.to({
                        [i * 3]: destPosition.array[cur * 3],
                        [i * 3 + 1]: destPosition.array[cur * 3 + 1],
                        [i * 3 + 2]: destPosition.array[cur * 3 + 2]
                    }, 3000 * Math.random());
                    tween.easing(TWEEN.Easing.Exponential.In);
                    tween.delay(3000);

                    tween.start();

                    tween.onUpdate(() => {
                        startPositions.needsUpdate = true;
                    });
                }
            } );
        }

        function render() {
            TWEEN.update();
            renderer.render(scene, camera);
            scene.rotation.y += 0.001;

            requestAnimationFrame(render);
        }
        create();
        render();
    </script>
</body>
</html>

Суммировать

Анимация частиц — это движение основных единиц, из которых состоит объект, что в 3D — это движение вершин.

Мы хотим реализовать анимацию частиц «звезды посылают благословения», то есть движение от вершины звезд к вершине слова благословение.

Вершины звезд можно генерировать случайным образом, используя BufferGeometry для создания соответствующей геометрии. Символ благословения — загрузить созданную 3D-модель и получить в ней позицию вершины.

С начальным и конечным положениями можно добиться анимации частиц.Значения x, y и z в процессе рассчитываются с использованием библиотеки анимации Tween.js, а также могут быть указаны временные функции, такие как ускорение и замедление.

Анимация частиц создает ощущение разрыва и реорганизации, и ее можно использовать для создания множества великолепных эффектов.

Я снял звезды с неба и хотел послать всем благословение. С этой анимацией 3D-частиц я заранее желаю всем счастливого Праздника Весны~