Построение геометрии вручную через Float32Array

В Three.js стандартные геометрические объекты, такие как BoxGeometry, SphereGeometry или PlaneGeometry, предоставляют удобные методы для быстрого создания сцен. Однако для более сложных или нестандартных форм необходима работа с буферной геометрией (BufferGeometry) и ручное определение вершин, нормалей и координат текстур с помощью массивов типа Float32Array. Это дает полный контроль над структурой объекта и позволяет оптимизировать производительность.


Основные компоненты BufferGeometry

BufferGeometry состоит из атрибутов (attributes), каждый из которых представляет собой массив данных:

  • position — координаты вершин в формате [x1, y1, z1, x2, y2, z2, ...].
  • normal — нормали вершин, используемые для освещения.
  • uv — координаты текстур в формате [u1, v1, u2, v2, ...].
  • index — индексный массив для повторного использования вершин при построении треугольников.

Атрибуты создаются с помощью Float32BufferAttribute:

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

В примере массив vertices содержит три вершины треугольника, каждая из которых описана тремя числами (x, y, z).


Индексирование вершин

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

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

Если объект содержит более 65535 вершин, следует использовать Uint32Array вместо Uint16Array.


Добавление нормалей

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

const normals = new Float32Array([
    0, 0, 1,
    0, 0, 1,
    0, 0, 1
]);
geometry.setAttribute('normal', new THREE.Float32BufferAttribute(normals, 3));

Нормали могут быть рассчитаны вручную или автоматически с помощью метода computeVertexNormals():

geometry.computeVertexNormals();

Координаты текстур

Атрибут uv определяет, как текстура проецируется на поверхность объекта. Значения находятся в диапазоне [0, 1]:

const uvs = new Float32Array([
    0, 0,
    1, 0,
    0, 1
]);
geometry.setAttribute('uv', new THREE.Float32BufferAttribute(uvs, 2));

Комбинируя uv с материалами MeshBasicMaterial или MeshStandardMaterial, можно создавать реалистичные текстурированные поверхности.


Построение сложных форм

Для сложных моделей вершины, нормали и UV-координаты создаются программно. Например, генерация плоскости размером width x height с segmentsX и segmentsY:

const width = 2;
const height = 2;
const segmentsX = 4;
const segmentsY = 4;

const vertices = [];
const normals = [];
const uvs = [];
const indices = [];

for (let y = 0; y <= segmentsY; y++) {
    for (let x = 0; x <= segmentsX; x++) {
        const posX = (x / segmentsX - 0.5) * width;
        const posY = (y / segmentsY - 0.5) * height;
        vertices.push(posX, posY, 0);
        normals.push(0, 0, 1);
        uvs.push(x / segmentsX, y / segmentsY);
    }
}

for (let y = 0; y < segmentsY; y++) {
    for (let x = 0; x < segmentsX; x++) {
        const a = x + (segmentsX + 1) * y;
        const b = x + (segmentsX + 1) * (y + 1);
        const c = (x + 1) + (segmentsX + 1) * (y + 1);
        const d = (x + 1) + (segmentsX + 1) * y;

        indices.push(a, b, d);
        indices.push(b, c, d);
    }
}

const geometry = new THREE.BufferGeometry();
geometry.setAttribute('position', new THREE.Float32BufferAttribute(vertices, 3));
geometry.setAttribute('normal', new THREE.Float32BufferAttribute(normals, 3));
geometry.setAttribute('uv', new THREE.Float32BufferAttribute(uvs, 2));
geometry.setIndex(indices);

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


Производительность и оптимизация

  • Использование индексов уменьшает объем данных и ускоряет рендеринг.
  • Float32Array эффективен для GPU и обеспечивает совместимость с WebGL.
  • Компактная организация атрибутов (например, хранение цветов и нормалей вместе с вершинами в одном массиве) снижает количество вызовов рендеринга.
  • Для динамически изменяемых объектов можно использовать geometry.attributes.position.needsUpdate = true после изменения вершин.

Примеры применения

  • Процедурные ландшафты и водные поверхности.
  • Пользовательские модели с нестандартными полигонами.
  • Реализация визуальных эффектов на основе деформаций вершин (vertex shader).
  • Высокопроизводительные сцены с большим количеством объектов.

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