Индексированная геометрия: index и экономия памяти

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


Основы BufferGeometry и вершин

В Three.js базовой единицей геометрии является BufferGeometry. Она хранит массивы данных о вершинах, нормалях, текстурных координатах и цветах. Например:

const geometry = new THREE.BufferGeometry();
const vertices = new Float32Array([
    0, 0, 0,
    1, 0, 0,
    0, 1, 0,
    1, 1, 0
]);
geometry.setAttribute('position', new THREE.BufferAttribute(vertices, 3));

Здесь каждая вершина состоит из трёх чисел (x, y, z). Для четырёх вершин потребуется 12 значений. Если строить множество треугольников с повторяющимися вершинами, обычный массив вершин быстро растет.


Механизм индексирования

Индексированная геометрия решает проблему повторяющихся вершин с помощью массива index. Этот массив хранит индексы вершин, которые формируют треугольники:

const indices = new Uint16Array([
    0, 1, 2,
    2, 1, 3
]);
geometry.setIndex(new THREE.BufferAttribute(indices, 1));

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

Преимущества:

  • Экономия памяти: каждая вершина хранится один раз.
  • Ускорение рендеринга: GPU эффективнее работает с индексированными массивами.
  • Упрощение обновлений геометрии: достаточно изменить вершину один раз, чтобы повлиять на все треугольники, где она используется.

Типы индексов

Three.js поддерживает различные типы массивов индексов, в зависимости от числа вершин:

  • Uint16Array — для моделей до 65 536 вершин.
  • Uint32Array — для больших моделей, когда число вершин превышает 65 536.

Важно выбирать подходящий тип, чтобы не расходовать лишнюю память.

let indexArray;
if (vertices.length / 3 > 65535) {
    indexArray = new Uint32Array([...]);
} else {
    indexArray = new Uint16Array([...]);
}
geometry.setIndex(new THREE.BufferAttribute(indexArray, 1));

Использование с атрибутами

Индексированная геометрия автоматически взаимодействует с любыми атрибутами, установленными через setAttribute:

const colors = new Float32Array([
    1, 0, 0,   // красная
    0, 1, 0,   // зеленая
    0, 0, 1,   // синяя
    1, 1, 0    // желтая
]);
geometry.setAttribute('color', new THREE.BufferAttribute(colors, 3));

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


Примеры практического применения

1. Куб с индексами

const geometry = new THREE.BoxGeometry(1, 1, 1);
console.log(geometry.index); // Uint16Array с 36 элементами (12 треугольников)

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

2. Сложные модели

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


Изменение индексированной геометрии

Индексы можно изменять динамически для анимации или генеративных сцен:

geometry.index.array[0] = 2;  // меняем первый треугольник
geometry.index.needsUpdate = true;  // уведомляем рендерер о изменении

Установка needsUpdate обязательна, иначе GPU продолжит использовать старые данные.


Оптимизация и рекомендации

  1. Использовать индексы всегда, когда вершины повторяются.
  2. Выбирать подходящий тип массива (Uint16Array или Uint32Array).
  3. Минимизировать пересечения атрибутов: если вершины имеют уникальные свойства, индексы не дают экономии.
  4. Группировать геометрию: при большом количестве объектов выгодно объединять геометрии с помощью BufferGeometryUtils.mergeBufferGeometries для уменьшения draw calls.

Индексированная геометрия в Three.js — это ключевой инструмент для управления памятью и производительностью. Понимание работы index позволяет создавать сложные сцены с миллионами вершин без лишней нагрузки на GPU и без дублирования данных в памяти.