BufferGeometry
顶点数据的容器:位置、法线、纹理坐标、颜色与索引都存在这里,最终上传为 GPU 缓冲区。
属性
| 属性 | 说明 |
|---|---|
attributes | 属性字典,键名即着色器里的 attribute 名,如 position、normal、uv |
index | 索引缓冲区,复用顶点、减少重复;为 null 时按顺序取顶点 |
groups | 面片分组,配合多材质使用 |
drawRange | 实际绘制的顶点或索引区间,可用于只画一部分 |
boundingBox / boundingSphere | 包围体,参与视锥裁剪与拾取,未计算时可能为 null |
morphAttributes | 变形目标属性 |
核心方法
| 方法 | 说明 |
|---|---|
setAttribute(name, attribute) | 写入属性,值为 BufferAttribute 实例 |
setIndex(index) | 设置索引,可传数组或 BufferAttribute |
setFromPoints(points) | 用点数组生成 position 属性,适合线与点云 |
computeVertexNormals() | 按三角面重算法线,顶点位置改动后必须调用 |
computeBoundingBox() / computeBoundingSphere() | 重算包围体,位置整体变化后要更新 |
applyMatrix4(matrix) | 把所有顶点乘上一个变换矩阵 |
toNonIndexed() | 返回展开后的非索引几何体,便于逐面操作 |
setDrawRange(start, count) | 限定绘制区间,常用于逐步显示轨迹 |
dispose() | 释放 GPU 缓冲区 |
手动构造一份几何体
const geometry = new THREE.BufferGeometry();
// 每个顶点三个分量,共三个顶点
const positions = new Float32Array([
-0.5, -0.5, 0,
0.5, -0.5, 0,
0.0, 0.5, 0
]);
geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3));
geometry.computeVertexNormals();
geometry.computeBoundingSphere();
BufferAttribute 的第二个参数是每个顶点占几个分量:
位置与法线是 3,纹理坐标是 2,颜色是 3 或 4。写错会导致着色器读到的数据整体错位,
表现是模型扭曲或纹理乱飞。
索引与顶点复用
索引让多个三角面共享同一批顶点。一个由 8 个顶点构成的立方体,非索引写法需要 36 个顶点, 索引写法只要 8 个顶点加 36 个索引。顶点属性越多,索引带来的节省越明显。
const box = new THREE.BoxGeometry(1, 1, 1);
console.log(box.index !== null); // true
console.log(box.attributes.position.count); // 24
const flat = box.toNonIndexed();
console.log(flat.attributes.position.count); // 36
动态更新顶点
const positions = geometry.attributes.position;
// 标记为动态用途,驱动层会采用适合频繁写入的缓冲策略
positions.setUsage(THREE.DynamicDrawUsage);
renderer.setAnimationLoop(function () {
for (let i = 0; i < positions.count; i++) {
const x = positions.getX(i);
positions.setY(i, Math.sin(x * 4 + clock.elapsedTime * 2) * 0.4);
}
positions.needsUpdate = true; // 关键:否则 GPU 侧不会重新上传
renderer.render(scene, camera);
});
只改了数据却没有置 needsUpdate,画面不会变化。这是动态几何最常见的疏漏。
如果整体替换成新的 Float32Array,还需要重新 setAttribute,
仅置标志位不够。
用 drawRange 做逐步显示
轨迹动画不一定要每帧重建几何体。预先写入整条路径,再用
setDrawRange(0, n) 逐步增加绘制数量,就能得到生长效果,
数据只上传一次。点数超过十万时,这种做法的收益非常明显。
释放
geometry.dispose() 会释放显存中的顶点与索引缓冲。
从场景移除网格并不会自动调用它。切换图层、重建几何体之前,
先销毁旧的那份,否则显存会持续累积。
熟悉的几何体优先用内置的。BoxGeometry、PlaneGeometry、
SphereGeometry 已经处理好索引、法线与 UV。
只有表达非规则数据(点云、地形、自定义网格)时才需要手写 BufferGeometry。
经纬志(2026)。《BufferGeometry》。https://www.jingweizhi.com/three/api/buffer-geometry.html(访问日期 2026-11-03)。