Схема анимации Three.js

WebGL three.js
Схема анимации Three.js

Автор этой статьи Чен Шуйи

Источник изображения Pixabay, автор Arek Socha

задний план

Three.js (далее Three) как 3D-библиотека не только снижает стоимость изучения OpenGL и WebGL, но и значительно улучшает интерфейсную визуализацию, чтобы предоставить пользователям более реальный и захватывающий опыт. Как мы все знаем, Three — это больше о создании «3D-мира» с 3D-моделью + проекционная камера + взаимодействие с пользователем.

Этот альбом, используйте свои глаза, чтобы «слушать»В том случае, когда зрение может предоставить только «2D-срезы», необходимо создать «3D-эффекты». Чтобы получить наилучшее визуальное впечатление, сложно сделать это только с текстурами, поэтому я воспользовался возможностью, чтобы изучить решения Three для движения.

Движение часто относительно, и сутью движения может быть «движение объекта» или «движение камеры».Объектная анимацияианимация камерыДинамическое исследование Трех описано выше.

Три основы

камера

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

const camera = THREE.PerspectiveCamera(fov, aspect, near, far);
параметр значение По умолчанию
fov fov — вертикальный (не горизонтальный!) угол раскрытия зрительного органа, а у человека угол обзора близок к 180 градусам. Это значение можно установить в соответствии с углом обзора, требуемым для конкретной сцены. 45
aspect             Определяет отношение размера по горизонтали к размеру по вертикали результата визуализации. Это значение обычно устанавливается равным соотношению сторон размера окна. window.innerWidth / window.innerHeight
near Указывает, насколько близко можно увидеть объект. Это значение обычно невелико. 0.1
far Указывает, насколько далеко можно увидеть объект. Это зависит от настройки, слишком много приведет к слишком большому рендерингу, слишком маленькое может быть не видно. 1000

ps:В Three нет понятия «единица длины», и ее значения рассчитываются по соотношению, поэтому упомянутые здесь 0,1 или 1000 имеют не конкретное значение, а относительную длину.

相机

Можно видеть, что при настройке соответствующих параметров камеры перспективы то, что в конечном итоге отображается на экране, находится вnearприбытьfarмежду, согласноfovзначение и расстояние до объектаdОпределите высоту рендеринга, а затем передайтеaspectзначение для определения ширины рендеринга.

Сцена сцены

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

const scene = new THREE.Scene();

Да, создать сцену так просто.

Group

Чтобы различать объекты сцены по размерности группы, мы также можем добавить в сцену Группу. С группой проще манипулировать классом объектов.
Например, создайтеstoneGroupи добавьте в сцену:

const stoneGroup = new THREE.Group();
stoneGroup.name = 'stoneGroup';

scene.add(stoneGroup);

Назовите группу, что позволит нам получить соответствующую группу по имени:

const group = scene.getObjectByName(name);

Геометрия

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

Проверьте геометрию, предоставленную Three

Если вы видите геометрию, предоставленную Three, вы можете увидеть, что в некоторых геометриях она обеспечиваетGeometeryиBufferGeometeryВерсия, о разнице между ними, можно посмотреть здесьотвечать

Общий смысл заключается в том, что при использовании версии геометрии с буфером данные, описывающие объект, будут храниться в буфере по сравнению с обычной геометрией, что снижает потребление памяти и циклы ЦП. Использование геометрии, безусловно, удобно для новичков благодаря методам, которые они предоставляют.

Создать геометрию:

// 创建立方体,传入长、宽和高
var cubeGeometry = new THREE.CubeGeometry(40, 40, 40);
// 创建球体,传入半径、宽片段数量和高片段数量
var sphereGeometry = new THREE.SphereGeometry(20, 100, 100);

Материал

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

название описывать
MeshBasicMaterial Базовый материал, определяющий простые цвета или каркасы геометрии.
MeshPhongMaterial Влияет на освещение, используется для создания блестящих объектов
MeshLambertMaterial Влияет на освещение, используется для создания неосвещенных объектов
MeshDepthMaterial Решите, как тонировать сетку, исходя из того, насколько далеко находится камера.

Создайте материал:

var basicMaterial = new THREE.MeshBasicMaterial({ color: 0x666666 });
var lambertMaterial = new THREE.MeshLambertMaterial({ color: 0x666666 });
var phongMaterial = new THREE.MeshPhongMaterial({ color: 0x666666 });
var wireMaterial = new THREE.MeshBasicMaterial({ wireframe: true, color: 0x666666 });

material

Для получения дополнительных материалов и сопутствующей информации вы можете проверитьматериал

Сетчатый объект сетки

Его нужно добавить в сцену, а также полагаться на Mesh. Сетка используется для определения того, как связаны материал и геометрия.При создании объекта сетки можно применить один или несколько материалов и геометрию.

Создайте объекты-сетки с одинаковой геометрией и разными материалами:

var cube = new THREE.Mesh(cubeGeometry, basicMaterial);
var cubePhong = new THREE.Mesh(cubeGeometry, phongMaterial);
scene.add(cube, cubePhong);

Создайте объекты сетки с тем же материалом и другой геометрией:

var cube = new THREE.Mesh(cubeGeometry, basicMaterial);
var sphere = new THREE.Mesh(sphereGeometry, basicMaterial);
scene.add(cube, sphere);

Создайте объект-сетку с геометрией из нескольких материалов:

var phongMaterial = new THREE.MeshPhongMaterial({ color: 0x666666 });
var cubeMeshPhong = new THREE.Mesh(cubeGeometry, cubePhongMaterial);
var cubeMeshWire = new THREE.Mesh(cubeGeometry, wireMaterial);
// 网格对象新增材质
cubeMeshPhong.add(cubeMeshWire);
scene.add(cubeMeshPhong);

Рендерер

Со сценой и камерой нам также нужен рендерер для рендеринга соответствующей сцены с видимой соответствующей камерой, поэтому рендерер должен передать параметры сцены и камеры.

// 抗锯齿、canvas 是否支持 alpha 透明度、preserveDrawingBuffer 是否保存 BUFFER 直到手动清除
const renderer = new THREE.WebGLRenderer({
    antialias: true, alpha: true, preserveDrawingBuffer: true
});
renderer.setSize(this.width, this.height);
renderer.autoClear = true;
// 清除颜色,第二个参数为 0 表示完全透明,适用于需要透出背景的场景
renderer.setClearColor(0x000000, 0);
renderer.setPixelRatio(window.devicePixelRatio);

Для того, чтобы увидеть сцену после обновления камеры, нужно добавить в цикл рендеринга

renderer.render(scene, camera);

С помощью сцены камеры и рендерера на месте мы уже можем увидеть начальные эффекты. Но в трехмерном мире стационарные объекты скучны. Поэтому мы попытались добавить анимационные эффекты.

Объектная анимация

Animations

Three предоставляет ряд методов для анимации.

параметр значение
AnimationMixer Действует как микшер анимации для определенного объекта и может управлять всеми анимациями для этого объекта.
AnimationAction             Укажите соответствующий фрагмент для хранения проигрывателем серии поведений, которые используются для указания скорости анимации, типа цикла и т. д.
AnimationClip выражатьмногоразовыйФрагмент поведения анимации, используемый для указания анимационного эффекта анимации (увеличение масштаба, перемещение вверх и вниз и т. д.).
KeyframeTrack Связанная со временем последовательность кадров, переданная во времени и значении, применяемая к свойствам указанного объекта. В настоящее время естьBooleanKeyframeTrack VectorKeyframeTrackЖдать.

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

Создайте микшер анимации для определенного объекта:

// 创建纹理
const texture = new THREE.TextureLoader().load(img.src);
// 使用纹理创建贴图
const material = new THREE.SpriteMaterial({ map: texture, color: 0x666666 });
// 使用贴图创建贴图对象
const stone = new THREE.Sprite(material);
// 为贴图对象创建动画混合器
const mixer = new THREE.AnimationMixer(stone);

Создайте клип поведения анимации:

const getClip = (pos = [0, 0, 0]) => {
    const [x, y, z] = pos;
    const times = [0, 1]; // 关键帧时间数组,离散的时间点序列
    const values = [x, y, z, x, y + 3, z]; // 与时间点对应的值组成的数组
    // 创建位置关键帧对象:0时刻对应位置0, 0, 0   10时刻对应位置150, 0, 0
    const posTrack = new THREE.VectorKeyframeTrack('stone.position', times, values);
    const duration = 1;
    return new THREE.AnimationClip('stonePosClip', duration, [posTrack]);
};

Создайте проигрыватель анимации, чтобы определить, как будет вести себя анимация:

const action = mixer.clipAction(getClip([x, y, z]));
action.timeScale = 1; // 动画播放一个周期的时间
action.loop = THREE.LoopPingPong; // 动画循环类型
action.play(); // 播放

Обновите микшер в отрисовке цикла, чтобы убедиться, что анимация выполняется:

animate() {
    // 更新动画
    const delta = this.clock.getDelta();
    mixer.update(delta);
    
    requestAnimationFrame(() => {
        animate();
    });
}

image

codepen

Текстурная анимация

С помощью анимации мы можем легко манипулировать некоторыми свойствами объектов. Но некоторые анимации, связанные с текстурой, сложно реализовать с помощью Animation, например:

箭头动图

Вышеприведенная картина не может быть достигнута путем изменения положения, размера и других атрибутов объекта. Итак, есть еще одно решение — текстурная анимация.

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

Во-первых, у нас есть диаграмма последовательности:

箭头序列图

Загружается как текстура и добавляется в сцену:

const arrowTexture = new THREE.TextureLoader().load(Arrow);
const material = new THREE.SpriteMaterial({ map: arrowTexture, color: 0xffffff });
const arrow = new THREE.Sprite(material);
scene.add(arrow);

утверждениеTextAnimatorОбъект, реализующий смещение текстуры:

function TextureAnimator(texture, tilesHoriz, tilesVert, numTiles, tileDispDuration) {
    // 纹理对象通过引用传入,之后可以直接使用update方法更新纹理位置
    this.tilesHorizontal = tilesHoriz;
    this.tilesVertical = tilesVert;
    // 序列图中的帧数
    this.numberOfTiles = numTiles;
    texture.wrapS = THREE.RepeatWrapping;
    texture.wrapT = THREE.RepeatWrapping;
    texture.repeat.set(1 / this.tilesHorizontal, 1 / this.tilesVertical);

    // 每一帧停留时长
    this.tileDisplayDuration = tileDispDuration;

    // 当前帧停留时长
    this.currentDisplayTime = 0;

    // 当前帧
    this.currentTile = 0;

    // 更新函数,通过这个函数对纹理位移进行更新
    this.update = (milliSec) => {
        this.currentDisplayTime += milliSec;
        while (this.currentDisplayTime > this.tileDisplayDuration) {
            this.currentDisplayTime -= this.tileDisplayDuration;
            this.currentTile++;
            if (this.currentTile === this.numberOfTiles) { this.currentTile = 0; }
            const currentColumn = this.currentTile % this.tilesHorizontal;
            texture.offset.x = currentColumn / this.tilesHorizontal;
            const currentRow = Math.floor(this.currentTile / this.tilesHorizontal);
            texture.offset.y = currentRow / this.tilesVertical;
        }
    };
}
// 传入一个一行里有 13 帧的序列图,每张序列图停留 75ms
const arrowAni = new TextureAnimator(arrowTexture, 13, 1, 13, 75);

Обновите отрисовку цикла, чтобы обеспечить выполнение анимации:

arrowAni.update(delta);

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

codepen

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

Three также обеспечивает крутую анимацию частиц, используя унаследованную от Object3DPointsреализация класса. С классом Points мы можем легко визуализировать геометрию в виде набора частиц и управлять ими.

Создать частицы

Чтобы создать частицы, нам сначала нужно создать материал для частиц, вы можете использоватьPointsMaterialСоздание материалов частиц.

const texture = new THREE.TextureLoader().load('https://p1.music.126.net/jgzbZtWZhDet2jWzED8BTw==/109951164579600342.png');

material = new THREE.PointsMaterial({
  color: 0xffffff,
  // 映射到材质上的贴图
  map: texture,
  size: 2,
  // 粒子的大小是否和其与摄像机的距离有关,默认值 true
  sizeAttenuation: true,
});

// 开启透明度测试,透明度低于0.5的片段会被丢弃,解决贴图边缘感问题
material.alphaTest = 0.5;

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

var particles = new THREE.Points( geometry, material );

Если вы пройдете вBoxGeometryВы можете получить набор таких частиц

cube粒子

также может основываться на поступающемShapeполучить такой набор частиц

fish粒子

движение частиц

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

tween.js

tween.js — это небольшая библиотека JS, которую мы можем использовать для объявления изменений в наших анимациях. С tween.js нам не нужно заботиться о промежуточных состояниях движения, только о частицах:

  • отправная точка
  • конечная позиция
  • Эффект легкости
// srcPosition, targetPosition;
tweens.push(new TWEEN.Tween(srcPosition).easing(TWEEN.Easing.Exponential.In));
// tweens最终位置、缓动时间
tweens[0].to(targetPosition, 5000);
tweens[0].start();、

codepen

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

анимация камеры

Камера действует как человеческий глаз в 3D-пространстве, поэтому естественное движение камеры обеспечивает естественное и плавное взаимодействие.

Controls

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

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

// 创建轨迹
const controls = new THREE.OrbitControls(this.camera, this.renderer.domElement);
controls.enabled = !0;
controls.target = new THREE.Vector3();
controls.minDistance = 0;
controls.maxDistance = 2000;
controls.minPolarAngle = Math.PI / 2;
controls.maxPolarAngle = Math.PI / 2;
// 禁用缩放
controls.enableZoom = !1;
// 禁用旋转
controls.enableRotate !1;
controls.panSpeed = 2;

// 修改控件的默认触摸选项,设置为单指双指都为平移操作
controls.touches = {
    ONE: THREE.TOUCH.PAN,
    TWO: THREE.TOUCH.PAN,
};

this.scene.add(this.camera);

OrbitControl также позволяет нам установить демпфирование.Установка этого значения означает, что чем ближе значение к 1, тем труднее перетаскивать.После включения демпфирования нам нужно вручную обновить управление.

controls.enableDamping = !0;
controls.dampingFactor = 0.2;

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

// this method is exposed, but perhaps it would be better if we can make it private...
this.update = function () {

	// ...

	return function update() {

		// ...

		// 平移

		if ( scope.enableDamping === true ) {
		    // 开启阻尼后会在原本的位移上乘上一个权重
		    scope.target.addScaledVector( panOffset, scope.dampingFactor );

		} else {

			scope.target.add( panOffset );

		}

		// ...

		if ( scope.enableDamping === true ) {

			sphericalDelta.theta *= ( 1 - scope.dampingFactor );
			sphericalDelta.phi *= ( 1 - scope.dampingFactor );

            // 如果没有人为操作,随着时间推移,panOffset会越来越小
			panOffset.multiplyScalar( 1 - scope.dampingFactor );

		} else {

			sphericalDelta.set( 0, 0, 0 );

			panOffset.set( 0, 0, 0 );

		}

		// ...

	};

}();

Чиновник также предоставляет элементы управленияпримерДля справки.

Движущаяся линия камеры

Если вы не используете элементы управления, вы просто перемещаете камеру из одной точки в другую. Для более плавной и естественной траектории камеры рекомендуется использовать кривую Безье.

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

二阶贝塞尔曲线

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

贝塞尔公式

По этой формуле нам нужно только определить три точки p0, p1 и p2, и мы можем получить определенную кривую в единицу времени.

Однако что делать с координатными точками?

// 获得贝塞尔曲线
function getBezier(p1, p2) {
    // 在指定范围内随机生成一个控制点
    const cp = {
        x: p1.x + Math.random() * 100 + 200,
        z: p2.z + Math.random() * 200,
    };

    let t = 0;
    // 贝塞尔曲线公式,根据时间确定点的位置
    return (deltat) => {
        if (t >= 1) return [p2.x, p2.y];
        t += deltat;
        if (t > 1) t = 1;

        const { x: x1, z: z1 } = p1;
        const { x: cx, z: cz } = cp;
        const { x: x2, z: z2 } = p2;
        const x = (1 - t) * (1 - t) * x1 + 2 * t * (1 - t) * cx + t * t * x2;
        const z = (1 - t) * (1 - t) * z1
            + 2 * t * (1 - t) * cz + t * t * z2;

        return [x, z];
    };
}
const bezier = getBezier(p1, p2);

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

Поскольку кривые Безье являются кривыми, зависящими от времени, для обновления положения камеры в каждом цикле рендеринга требуется время.

animation() {
    const [x, z] = bezier(clock.getDelta());
    camera.position.x = x;
    camera.position.z = z;
    
    requestAnimationFrame(() => {
            animate();
    });
}

резюме

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

Этот документ знакомит сAnimationпростое перемещение предметов,TextureРеализовать анимацию текстур и использоватьPointsСхема анимации объектов на основе частиц; на основеControlsи кривые Безье для схем анимации камеры.

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

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

использованная литература

Эта статья была опубликована сКоманда внешнего интерфейса NetEase Cloud Music, Любое несанкционированное воспроизведение статьи запрещено. Мы всегда нанимаем, если вы готовы сменить работу и вам нравится облачная музыка, тоПрисоединяйтесь к нам!