Класс Geometry: атрибуты и буферы вершин

Geometry в PixiJS представляет низкоуровневую структуру данных для описания геометрии объекта: вершин, индексов и произвольных атрибутов, передаваемых в шейдеры. Этот класс используется совместно с Mesh и позволяет полностью контролировать процесс рендеринга через WebGL.

В отличие от Graphics, который ориентирован на векторную отрисовку высокого уровня, Geometry работает напрямую с буферами GPU. Это фундамент для создания кастомных мешей, систем частиц, процедурной геометрии и нестандартных шейдерных эффектов.


Архитектура Geometry

Класс Geometry хранит:

  • набор атрибутов вершин
  • индексный буфер
  • ссылки на WebGL-буферы
  • информацию о структуре данных (stride, offset, типы)

Каждая геометрия описывает, как именно данные расположены в памяти GPU и как они должны интерпретироваться шейдером.

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

  • Buffer — контейнер данных (обычно Float32Array, Uint16Array и др.)
  • Attribute — описание того, как читать данные из буфера
  • индексный буфер — порядок соединения вершин в треугольники

Создание Geometry

Базовое создание геометрии:

import { Geometry } from 'pixi.js';

const geometry = new Geometry();

Далее в неё добавляются атрибуты и индексы.


Атрибуты вершин

Атрибут — это описание данных, которые будут переданы в вершинный шейдер. Каждый атрибут связан с именем, совпадающим с именем переменной в GLSL.

Пример добавления позиции:

geometry.addAttribute(
  'aPosition',
  new Float32Array([
    0, 0,
    100, 0,
    100, 100,
    0, 100
  ]),
  2
);

Параметры addAttribute

Метод addAttribute принимает:

  • id — имя атрибута (должно совпадать с GLSL)
  • buffer — данные или экземпляр Buffer
  • size — количество компонентов на вершину (2 для vec2, 3 для vec3)
  • normalized — нормализовать ли данные
  • type — тип данных (по умолчанию FLOAT)
  • stride — шаг между вершинами
  • start — смещение

Минимальный вариант:

geometry.addAttribute('aPosition', vertices, 2);

Буферы (Buffer)

Если требуется более гибкий контроль, создаётся явный буфер:

import { Buffer } from 'pixi.js';

const vertexBuffer = new Buffer(new Float32Array([
  0, 0,
  100, 0,
  100, 100,
  0, 100
]));

geometry.addAttribute('aPosition', vertexBuffer, 2);

Статические и динамические буферы

Буфер может быть:

  • статическим (данные не меняются)
  • динамическим (обновляются каждый кадр)
const dynamicBuffer = new Buffer(data, true);

Второй аргумент true указывает, что буфер динамический.

Обновление данных:

dynamicBuffer.update();

При работе с анимациями или частицами динамические буферы обязательны для производительности.


Индексный буфер

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

Добавление индексов:

geometry.addIndex([
  0, 1, 2,
  0, 2, 3
]);

Или через типизированный массив:

geometry.addIndex(new Uint16Array([
  0, 1, 2,
  0, 2, 3
]));

Преимущества индексов

  • уменьшение объёма данных
  • снижение нагрузки на GPU
  • более эффективный кэш вершин

Для сложной геометрии индексный буфер критически важен.


Несколько атрибутов в одной геометрии

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

Пример с цветом:

geometry
  .addAttribute('aPosition', vertices, 2)
  .addAttribute('aColor', new Float32Array([
    1, 0, 0,
    0, 1, 0,
    0, 0, 1,
    1, 1, 0
  ]), 3);

Вершинный шейдер должен содержать:

attribute vec2 aPosition;
attribute vec3 aColor;

Каждый атрибут синхронизируется по индексу вершины.


Interleaved-буферы

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

Пример структуры:

[x, y, r, g, b,  x, y, r, g, b, ...]

Создание:

const interleaved = new Float32Array([
  0, 0, 1, 0, 0,
  100, 0, 0, 1, 0,
  100, 100, 0, 0, 1,
  0, 100, 1, 1, 0
]);

const buffer = new Buffer(interleaved);

geometry
  .addAttribute('aPosition', buffer, 2, false, undefined, 20, 0)
  .addAttribute('aColor', buffer, 3, false, undefined, 20, 8);

Пояснение stride и offset

  • stride = 20 байт (5 float × 4 байта)
  • offset = 0 — позиция начинается с начала
  • offset = 8 — цвет начинается после двух float (2 × 4 байта)

Interleaved-буферы:

  • уменьшают количество bind-вызовов
  • лучше используют кеш GPU
  • подходят для сложных сцен

Изменение геометрии во время работы

Обновление массива:

vertices[0] += 1;
vertexBuffer.update();

Если геометрия динамическая, обновления отправляются в GPU без пересоздания буфера.

Для частых изменений рекомендуется:

  • минимизировать объём передаваемых данных
  • обновлять только изменённые участки
  • избегать постоянного пересоздания Geometry

Удаление и очистка

Геометрия хранит GPU-ресурсы, поэтому важно освобождать память:

geometry.destroy();

Это удаляет связанные буферы и WebGL-объекты.


Связь с Mesh

Geometry используется вместе с Mesh:

import { Mesh, Shader } from 'pixi.js';

const mesh = new Mesh(geometry, shader);

Mesh:

  • использует геометрию
  • связывает её с шейдером
  • управляет трансформациями
  • интегрируется в сцену

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


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

Минимизация атрибутов

Каждый атрибут увеличивает объём данных. Передача только необходимых параметров снижает нагрузку.

Использование индексов

Повторное использование вершин сокращает память и ускоряет рендеринг.

Interleaving

Чередование атрибутов в одном буфере снижает переключения состояния WebGL.

Динамические обновления

  • избегать полного пересоздания Geometry
  • использовать Buffer.update()
  • обновлять данные пакетно

Расширенные возможности

Кастомные типы данных

Можно использовать:

  • Float32Array
  • Uint16Array
  • Uint32Array
  • Int8Array

Тип должен соответствовать параметру type.

Instanced-рендеринг

PixiJS поддерживает инстансинг через дополнительные атрибуты с делителем (instance-атрибуты). Это позволяет:

  • рендерить тысячи объектов за один draw call
  • передавать данные трансформации через атрибуты

Внутреннее устройство

Geometry хранит:

  • attributes — словарь атрибутов
  • indexBuffer
  • список WebGL-контекстов
  • версии буферов для отслеживания изменений

При первом использовании данные отправляются в GPU. При обновлении версии увеличиваются, и PixiJS повторно загружает изменённые буферы.


Типичный рабочий цикл

  1. Создание Geometry
  2. Добавление атрибутов
  3. Добавление индексов
  4. Создание Shader
  5. Создание Mesh
  6. Добавление в сцену
  7. Обновление буферов при необходимости

Практическая структура простой геометрии

Минимальный пример прямоугольника:

const geometry = new Geometry()
  .addAttribute('aPosition', new Float32Array([
    -50, -50,
     50, -50,
     50,  50,
    -50,  50,
  ]), 2)
  .addIndex([0, 1, 2, 0, 2, 3]);

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


Geometry — ключевой инструмент для работы с низкоуровневой графикой в PixiJS. Он предоставляет прямой контроль над структурой вершинных данных, позволяет оптимизировать производительность и создавать сложные визуальные эффекты, недоступные при использовании высокоуровневых абстракций.