A-Frame, как фреймворк для создания VR-приложений на базе WebGL,
строится поверх библиотеки Three.js. Это позволяет использовать
низкоуровневые структуры данных Three.js, включая
BufferGeometry, для создания кастомных 3D-объектов с
высокой производительностью. BufferGeometry представляет
собой массивы атрибутов вершин, нормалей, цветов и UV-координат,
хранящиеся в виде Float32Array или других TypedArray, что
позволяет GPU обрабатывать данные напрямую без промежуточных объектов
Geometry.
В A-Frame объекты с geometry обычно создаются через
<a-box>, <a-sphere> и другие
примитивы. Прямой доступ к BufferGeometry предоставляет
возможность строить уникальные формы и оптимизировать производительность
за счёт точного контроля над вершинными буферами.
Каждый элемент сцены A-Frame имеет свойство object3D,
которое является экземпляром THREE.Object3D. Для работы с
геометрией необходимо обратиться к дочернему объекту
mesh:
const box = document.querySelector('a-box');
const mesh = box.getObject3D('mesh'); // THREE.Mesh
const geometry = mesh.geometry; // THREE.BufferGeometry
После получения объекта BufferGeometry можно читать и
изменять данные напрямую:
// Доступ к атрибуту вершин
const positionAttr = geometry.attributes.position;
// Изменение позиции первой вершины
positionAttr.setXYZ(0, 1.0, 2.0, 3.0);
// Уведомление Three.js о необходимости обновления
positionAttr.needsUpdate = true;
Важно всегда выставлять needsUpdate = true, иначе
изменения не будут видны на рендере.
Для нестандартных объектов часто проще создать
BufferGeometry напрямую и присвоить её
mesh.geometry:
const geometry = new THREE.BufferGeometry();
// Массив вершин: 3 координаты на каждую вершину
const vertices = new Float32Array([
0, 0, 0, // вершина 1
1, 0, 0, // вершина 2
0, 1, 0 // вершина 3
]);
geometry.setAttribute('position', new THREE.BufferAttribute(vertices, 3));
// Создание меша с материалом
const material = new THREE.MeshStandardMaterial({ color: 0x00ff00 });
const mesh = new THREE.Mesh(geometry, material);
// Добавление в сцену A-Frame
const sceneEl = document.querySelector('a-scene');
sceneEl.object3D.add(mesh);
Такой подход позволяет создавать треугольники, полигоны и сложные модели без использования сторонних форматов.
Прямой доступ к BufferGeometry даёт возможность
реализовать анимацию на уровне вершин. Например, волнообразное движение
плоскости:
const geometry = mesh.geometry;
const position = geometry.attributes.position;
const count = position.count;
function animateWave(time) {
for (let i = 0; i < count; i++) {
const x = position.getX(i);
const y = position.getY(i);
position.setZ(i, Math.sin(x + time * 0.001) * 0.5);
}
position.needsUpdate = true;
requestAnimationFrame(animateWave);
}
animateWave(0);
Такой подход экономит ресурсы, так как обновляются только массивы вершин, а не весь объект сцены.
BufferGeometry поддерживает индексированные вершины
через geometry.setIndex(). Индексы позволяют повторно
использовать вершины, уменьшая размер массивов и нагрузку на GPU:
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));
// Индексы треугольников
const indices = new Uint16Array([
0, 1, 2,
2, 1, 3
]);
geometry.setIndex(new THREE.BufferAttribute(indices, 1));
Индексы особенно полезны для моделей с большим количеством повторяющихся вершин, например, сеток или ландшафтов.
Для корректного освещения важно рассчитывать нормали после изменения вершин:
geometry.computeVertexNormals();
Если в процессе анимации вершины изменяются, нормали следует пересчитывать каждый кадр или на ключевых этапах:
positionAttr.setXYZ(0, 1.0, 2.0, 3.0);
geometry.computeVertexNormals();
positionAttr.needsUpdate = true;
Прямой доступ к BufferGeometry позволяет создавать
пользовательские компоненты, которые динамически модифицируют форму
объектов:
AFRAME.registerComponent('wave-plane', {
schema: { speed: { type: 'number', default: 1.0 } },
init: function () {
this.mesh = this.el.getObject3D('mesh');
this.positions = this.mesh.geometry.attributes.position;
},
tick: function (time) {
const positions = this.positions;
for (let i = 0; i < positions.count; i++) {
const x = positions.getX(i);
positions.setZ(i, Math.sin(x + time * 0.001 * this.data.speed));
}
positions.needsUpdate = true;
this.mesh.geometry.computeVertexNormals();
}
});
Компонент можно использовать на
<a-plane wave-plane="speed: 2">, и он будет изменять
форму плоскости в реальном времени.
Атрибуты color и uv также доступны через
BufferGeometry.attributes. Примеры:
// Добавление цветов вершин
const colors = new Float32Array([
1, 0, 0, // красная вершина
0, 1, 0, // зелёная
0, 0, 1 // синяя
]);
geometry.setAttribute('color', new THREE.BufferAttribute(colors, 3));
// UV для текстурирования
const uvs = new Float32Array([
0, 0,
1, 0,
0, 1
]);
geometry.setAttribute('uv', new THREE.BufferAttribute(uvs, 2));
Материал должен учитывать вершинные цвета
(vertexColors: true) для корректного отображения.
BufferGeometry для объектов с большим
количеством вершин или динамическими изменениями.needsUpdate = true после изменения
атрибутов.Прямой доступ к BufferGeometry позволяет комбинировать
удобство A-Frame с гибкостью Three.js, обеспечивая максимальную
производительность и творческий контроль над 3D-моделями.