Клонирование и слияние геометрий

В библиотеке Three.js геометрии являются объектами, описывающими форму 3D-модели через вершины, грани, нормали и UV-координаты. Иногда возникает необходимость создавать точные копии существующих геометрий для многократного использования, не изменяя исходный объект. Для этого используется метод clone(), который создаёт глубокую копию объекта геометрии, включая все её атрибуты.

const boxGeometry = new THREE.BoxGeometry(1, 1, 1);
const clonedGeometry = boxGeometry.clone();

Важный момент: клонированная геометрия полностью независима от оригинала. Любые изменения в clonedGeometry не повлияют на boxGeometry.

Для поверхностного копирования, когда требуется лишь дублировать ссылку на объект без копирования всех данных, используется copy():

const copiedGeometry = new THREE.BufferGeometry().copy(boxGeometry);

Разница между clone() и copy() заключается в том, что clone() создаёт новый объект с собственными внутренними массивами данных, тогда как copy() позволяет заменить содержимое уже существующего объекта.


Объединение геометрий

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

Использование BufferGeometryUtils.mergeBufferGeometries

Three.js предоставляет утилиту BufferGeometryUtils, включающую функцию mergeBufferGeometries, которая принимает массив геометрий и возвращает одну объединённую:

import { BufferGeometryUtils } from 'three/examples/jsm/utils/BufferGeometryUtils.js';

const sphere = new THREE.SphereGeometry(0.5, 16, 16);
const box = new THREE.BoxGeometry(1, 1, 1);

const merged = BufferGeometryUtils.mergeBufferGeometries([sphere, box], false);

Второй параметр (false) указывает, что индексы не должны быть пересозданы для оптимизации. Если геометрии имеют общие атрибуты и индексы, их можно объединять с true, чтобы избежать дублирования вершин.

Особенности объединения:

  • Все геометрии должны быть BufferGeometry. Классические Geometry больше не поддерживаются в последних версиях Three.js.
  • Атрибуты (например, позиции, нормали, UV) должны иметь одинаковую структуру для корректного слияния.
  • При необходимости сохранить материалы для разных частей объединённого меша создаётся MeshFaceMaterial или используется массив материалов.

Применение матриц трансформаций при объединении

Перед объединением часто нужно преобразовать геометрии: переместить, повернуть или масштабировать. Для этого используется метод applyMatrix4(), позволяющий применить матрицу преобразования к геометрии:

const translationMatrix = new THREE.Matrix4().makeTranslation(2, 0, 0);
box.applyMatrix4(translationMatrix);

const mergedWithTransform = BufferGeometryUtils.mergeBufferGeometries([sphere, box], false);

Каждая геометрия сохраняет свои координаты в глобальном пространстве перед слиянием. Это особенно важно при создании сцен с множеством объектов на разных позициях.


Клонирование и слияние атрибутов

BufferGeometry хранит данные в виде атрибутов (position, normal, uv и других). При клонировании создаётся копия всех массивов, но при слиянии массивы атрибутов необходимо правильно конкатенировать.

Пример ручного объединения двух геометрий с учётом атрибутов:

const geometry1 = new THREE.BoxGeometry(1, 1, 1);
const geometry2 = new THREE.SphereGeometry(0.5, 16, 16);

// Получение атрибутов
const pos1 = geometry1.attributes.position.array;
const pos2 = geometry2.attributes.position.array;

// Создание нового Float32Array
const mergedPos = new Float32Array(pos1.length + pos2.length);
mergedPos.set(pos1);
mergedPos.set(pos2, pos1.length);

// Создание новой геометрии
const mergedGeometry = new THREE.BufferGeometry();
mergedGeometry.setAttribute('position', new THREE.BufferAttribute(mergedPos, 3));

Этот способ позволяет полностью контролировать процесс объединения, добавлять или модифицировать дополнительные атрибуты (например, цвета или веса вершин).


Клонирование с материалами и мешами

Клонирование геометрии часто используется вместе с клонированием Mesh:

const mesh = new THREE.Mesh(boxGeometry, new THREE.MeshStandardMaterial({ color: 0xff0000 }));
const clonedMesh = mesh.clone();
clonedMesh.position.set(2, 0, 0);
scene.add(clonedMesh);

Метод clone() для Mesh не только клонирует геометрию, но и материалы. При необходимости можно создать отдельный материал для клона:

const clonedMesh = mesh.clone();
clonedMesh.material = mesh.material.clone();

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


Практические рекомендации

  • Для динамических сцен с множеством однотипных объектов лучше использовать InstancedMesh, что эффективнее с точки зрения GPU, чем постоянное клонирование и слияние геометрий.
  • Слияние геометрий стоит применять там, где объекты не меняют свою форму или материал в процессе анимации.
  • Всегда проверять соответствие атрибутов при объединении, чтобы избежать ошибок при рендеринге.

Примеры типичных сценариев

  1. Создание большого ландшафта из блоков Используется слияние геометрий блоков в один объект для снижения количества draw calls.

  2. Многократное использование одного объекта Клонирование позволяет создать копии одного меша с различными позициями и ориентациями.

  3. Оптимизация статических сцен Несколько статичных элементов объединяются в одну геометрию, улучшая производительность WebGL.

Эти приёмы обеспечивают гибкость и эффективность при работе с 3D-сценами, особенно в проектах с большим количеством объектов.