Анимация по кривым: CatmullRomCurve3 и QuadraticBezierCurve3

Three.js предоставляет возможность создавать сложные траектории движения объектов в 3D-пространстве с помощью кривых. Наиболее часто используемые классы для этого — CatmullRomCurve3 и QuadraticBezierCurve3, позволяющие задавать плавные пути и реализовывать анимации с точным контролем положения объекта во времени.


CatmullRomCurve3

CatmullRomCurve3 реализует кривую типа Catmull-Rom, проходящую через заданный набор точек. Ключевой особенностью является гладкое прохождение через все контрольные точки, без необходимости ручного вычисления углов наклона или касательных.

const points = [
    new THREE.Vector3(0, 0, 0),
    new THREE.Vector3(10, 5, 0),
    new THREE.Vector3(20, 0, 5),
    new THREE.Vector3(30, 5, 0)
];

const curve = new THREE.CatmullRomCurve3(points);
  • closed: параметр, определяющий замкнута ли кривая (true или false). При замкнутой кривой последний сегмент соединяется с первым.
  • tension: число от 0 до 1, влияющее на “жёсткость” кривой. Значение 0 создаёт мягкие изгибы, 1 — более жёсткие.

Генерация точек для визуализации или анимации осуществляется с помощью метода getPoints() или getPoint(t):

const divisions = 50;
const curvePoints = curve.getPoints(divisions); // массив точек
const pointAtHalf = curve.getPoint(0.5);       // точка на середине кривой

Для анимации объекта вдоль кривой используется метод getPoint(t), где t изменяется от 0 до 1.

let t = 0;
function animate() {
    requestAnimationFrame(animate);
    t += 0.001;
    if (t > 1) t = 0;
    object.position.copy(curve.getPoint(t));
}

QuadraticBezierCurve3

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

const start = new THREE.Vector3(0, 0, 0);
const control = new THREE.Vector3(10, 15, 5);
const end = new THREE.Vector3(20, 0, 0);

const curve = new THREE.QuadraticBezierCurve3(start, control, end);

Методы getPoint(t) и getPoints(divisions) работают аналогично CatmullRomCurve3:

const points = curve.getPoints(20);
const midPoint = curve.getPoint(0.5);
  • t = 0 соответствует началу кривой, t = 1 — концу.
  • Кривая легко комбинируется с таймерами или requestAnimationFrame для создания анимаций.

Применение кривых для анимации

Анимация по кривой строится по принципу итеративного изменения параметра t с последующим обновлением позиции объекта:

let t = 0;
const speed = 0.002;

function animate() {
    requestAnimationFrame(animate);
    t += speed;
    if (t > 1) t = 0;
    object.position.copy(curve.getPoint(t));
    renderer.render(scene, camera);
}
animate();

Для более реалистичного движения используется вычисление касательной к кривой, позволяющей ориентировать объект вдоль пути:

const tangent = curve.getTangent(t).normalize();
object.lookAt(object.position.clone().add(tangent));

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


Визуализация кривой

Для отладки и визуального контроля траектории удобно использовать THREE.Line:

const points = curve.getPoints(100);
const geometry = new THREE.BufferGeometry().setFromPoints(points);
const material = new THREE.LineBasicMaterial({ color: 0xff0000 });
const line = new THREE.Line(geometry, material);
scene.add(line);
  • Использование большого числа точек (100–200) делает кривую более гладкой.
  • Цвет и толщина линии помогают различать несколько траекторий на одной сцене.

Комбинирование кривых

Можно создавать сложные траектории, комбинируя несколько кривых с помощью THREE.CurvePath:

const path = new THREE.CurvePath();
path.add(curve1);
path.add(curve2);
const points = path.getPoints(200);
  • Позволяет плавно соединять разные типы кривых.
  • Используется для маршрутов камер, движения персонажей и объектов сцены.

Плавность и скорость движения

По умолчанию движение по кривой с равномерным шагом t может выглядеть неровным, если сегменты кривой имеют разную длину. Для решения используют getUtoTmapping(u), который позволяет равномерно распределять движение по длине кривой:

const u = 0.5; // половина длины кривой
const t = curve.getUtoTmapping(u);
object.position.copy(curve.getPoint(t));

Это обеспечивает постоянную скорость движения независимо от кривизны траектории.


Практические советы

  • Для анимации транспорта и камер предпочтительно использовать CatmullRomCurve3, так как она естественно проходит через все контрольные точки.
  • Для дугового или точного манипулирования объектом на ограниченном сегменте удобно использовать QuadraticBezierCurve3.
  • Визуализация кривых значительно облегчает отладку сложных анимаций.
  • Использование касательных и метода lookAt делает движение более реалистичным и плавным.

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