学习Three.js--缓冲类型几何体(BufferGeometry)
前置核心说明
BufferGeometry 是 Three.js 中所有几何体的底层核心(BoxGeometry/SphereGeometry 等预设几何体均基于它构建),也是官方唯一推荐使用的几何体类型(旧版 Geometry 已被废弃)。
核心区别与优势(为什么用 BufferGeometry)
| 类型 | 核心特点 | 性能 | 官方态度 |
|---|---|---|---|
| BufferGeometry(缓冲几何体) | 顶点数据存储在「类型化数组」(Float32Array 等)中,直接对接 GPU 内存 | 极高(GPU 直接读取,无数据转换) | 主推,唯一维护 |
| Geometry(旧版几何体) | 顶点数据存储在普通数组中,需转换后才能给 GPU 使用 | 较低(多一层数据转换) | 废弃,不再维护 |
核心逻辑
BufferGeometry 本身是「空容器」,没有任何预设形状,你需要通过定义顶点数据(坐标、颜色、纹理坐标等)来「自定义任意几何形状」,核心是:
类型化数组(顶点数据)→ BufferAttribute(属性封装)→ BufferGeometry(绑定属性)→ 渲染对象(Mesh/Line/Points)
一、BufferGeometry 核心概念与基础用法
1. 核心术语解释
- 顶点(Vertex):3D 空间中的一个点,由 X/Y/Z 三个坐标值组成,是构成几何体的最基本单元;
- 类型化数组:如
Float32Array(32位浮点数组),专门用于存储顶点数据,比普通数组更节省内存、GPU 读取更快; - BufferAttribute:Three.js 对「类型化数组」的封装,告诉 Three.js 「数组中的数据如何分组解析」(比如每3个值为一组表示一个顶点坐标);
- 属性(Attribute):几何体的「数据维度」,如
position(顶点坐标)、color(顶点颜色)、uv(纹理坐标)、normal(法线)等,一个几何体可绑定多个属性。
2. 基础使用流程
以下是从「创建空几何体」到「渲染自定义形状」的完整流程
步骤1:创建空的 BufferGeometry 容器
// 语法:无参数,创建空的缓冲几何体
const geometry = new THREE.BufferGeometry();
步骤2:定义顶点数据(类型化数组)
顶点数据必须用 类型化数组(不能用普通数组),常用:
Float32Array:存储浮点型数据(坐标、颜色、UV 等,最常用);Uint16Array:存储无符号16位整数(索引数据)。
// 顶点坐标数据:每3个值为一组(X,Y,Z),表示一个顶点的3D坐标
// 示例:6个顶点,对应2个三角形(Mesh 默认按三角面渲染)
const vertices = new Float32Array([
0, 0, 0, // 顶点1:(0,0,0)
50, 0, 0, // 顶点2:(50,0,0)
0, 100, 0, // 顶点3:(0,100,0) → 第一个三角形(顶点1-2-3)
0, 0, 10, // 顶点4:(0,0,10)
0, 0, 100, // 顶点5:(0,0,100)
50, 0, 10 // 顶点6:(50,0,10) → 第二个三角形(顶点4-5-6)
]);
步骤3:创建 BufferAttribute
// 语法:new THREE.BufferAttribute(类型化数组, 组内元素数量, 是否归一化)
// 关键:itemSize=3 → 每3个值为一组(对应X/Y/Z坐标)
const positionAttribute = new THREE.BufferAttribute(vertices, 3);
| BufferAttribute 参数 | 类型 | 默认值 | 核心说明 |
|---|---|---|---|
array | TypedArray | — | 必传,存储顶点数据的类型化数组 |
itemSize | Number | — | 必传,每组的元素数量(坐标=3,颜色=3/4,UV=2) |
normalized | Boolean | false | 是否归一化数据(颜色数据常用,将0-255转为0-1) |
usage | Number | THREE.StaticDrawUsage | 数据使用方式(静态/动态,默认静态即可) |
步骤4:将属性绑定到几何体
// 语法:geometry.setAttribute(属性名, BufferAttribute对象)
// 核心:属性名必须是固定值,如 "position"(坐标)、"color"(颜色)、"uv"(纹理)
geometry.setAttribute('position', positionAttribute);
步骤5:创建材质和渲染对象
// 材质:MeshBasicMaterial 不受光照影响,适合调试
const material = new THREE.MeshBasicMaterial({
color: 0x00ff00, // 基础颜色(无顶点颜色时生效)
wireframe: false, // 是否显示线框(true=线框,false=实体)
side: THREE.DoubleSide // 双面渲染(避免背面不可见)
});
// 创建网格对象(将几何体+材质绑定)
const mesh = new THREE.Mesh(geometry, material);
// 添加到场景
scene.add(mesh);
二、BufferGeometry 核心参数与常用属性
1. 构造函数(无参数)
// 始终无参数,创建空几何体,后续通过 setAttribute 绑定数据
const geometry = new THREE.BufferGeometry();
2. 核心属性
| 属性名 | 类型 | 说明 | 示例 |
|---|---|---|---|
attributes | Object | 存储所有绑定的属性(position/color/uv 等) | geometry.attributes.position → 获取坐标属性 |
index | BufferAttribute | 索引缓冲区(优化顶点重复,下文详解) | geometry.setIndex(索引数组) |
boundingBox | Box3 | 几何体的包围盒(自动计算,用于碰撞检测/裁剪) | geometry.computeBoundingBox() → 计算包围盒 |
boundingSphere | Sphere | 几何体的包围球 | geometry.computeBoundingSphere() |
drawRange | Object | 渲染范围(只渲染部分顶点) | geometry.drawRange = { start: 0, count: 3 } → 只渲染前3个顶点 |
3. 核心方法
| 方法名 | 说明 | 示例 |
|---|---|---|
setAttribute(name, attribute) | 绑定属性到几何体 | geometry.setAttribute('position', attr) |
getAttribute(name) | 获取已绑定的属性 | geometry.getAttribute('position') |
removeAttribute(name) | 移除属性 | geometry.removeAttribute('color') |
setIndex(array) | 设置索引缓冲区 | geometry.setIndex(new Uint16Array([0,1,2])) |
computeBoundingBox() | 计算几何体包围盒 | geometry.computeBoundingBox() |
computeBoundingSphere() | 计算几何体包围球 | geometry.computeBoundingSphere() |
computeVertexNormals() | 计算顶点法线(让光照生效) | geometry.computeVertexNormals() |
dispose() | 销毁几何体(释放内存) | geometry.dispose() |
三、BufferGeometry 进阶用法
1. 索引缓冲区(Index):减少顶点重复
问题场景
绘制一个矩形(由两个三角面组成),直接定义顶点需要6个(重复2个):
(0,0,0)、(1,0,0)、(0,1,0)、(1,0,0)、(1,1,0)、(0,1,0)
索引解决方案
- 定义4个唯一顶点 + 6个索引(指定三角面的顶点顺序),节省内存:
// 步骤1:创建空几何体
const geometry = new THREE.BufferGeometry();
// 步骤2:定义4个唯一顶点(无重复)
const vertices = new Float32Array([
0, 0, 0, // 顶点0
1, 0, 0, // 顶点1
0, 1, 0, // 顶点2
1, 1, 0 // 顶点3
]);
const posAttr = new THREE.BufferAttribute(vertices, 3);
geometry.setAttribute('position', posAttr);
// 步骤3:定义索引(每3个值为一组,指定三角面的顶点索引)
// 第一个三角面:顶点0→1→2;第二个三角面:顶点1→3→2
const indices = new Uint16Array([
0, 1, 2,
1, 3, 2
]);
// 设置索引缓冲区
geometry.setIndex(new THREE.BufferAttribute(indices, 1));
// 步骤4:创建材质和网格
const material = new THREE.MeshBasicMaterial({ color: 0xff0000, side: THREE.DoubleSide });
const mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);
核心优势
- 顶点数量从6个减少到4个,数据量降低33%;
- 复杂几何体(如球体)可减少大量重复顶点,性能提升显著。
2. 顶点颜色(Color Attribute):每个顶点自定义颜色
核心逻辑
给几何体绑定 color 属性,材质开启 vertexColors: true,即可让每个顶点显示自定义颜色,三角面内自动渐变。
// 步骤1:创建空几何体 + 顶点坐标
const geometry = new THREE.BufferGeometry();
const vertices = new Float32Array([
0, 0, 0, // 顶点0
1, 0, 0, // 顶点1
0, 1, 0 // 顶点2
]);
geometry.setAttribute('position', new THREE.BufferAttribute(vertices, 3));
// 步骤2:定义顶点颜色(每3个值为一组,RGB,0-1范围)
const colors = new Float32Array([
1, 0, 0, // 顶点0:红色
0, 1, 0, // 顶点1:绿色
0, 0, 1 // 顶点2:蓝色
]);
geometry.setAttribute('color', new THREE.BufferAttribute(colors, 3));
// 步骤3:材质开启顶点颜色(关键)
const material = new THREE.MeshBasicMaterial({
vertexColors: true, // 启用顶点颜色(覆盖基础color)
side: THREE.DoubleSide
});
// 步骤4:创建网格
const mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);
效果
三角面会从红色顶点渐变到绿色,再渐变到蓝色,实现多彩渐变效果。
3. 法线属性(Normal):让光照生效
MeshStandardMaterial 等受光照影响的材质,需要「法线数据」才能计算光影,可手动定义或自动计算:
// 方式1:自动计算法线(推荐,适合简单几何体)
geometry.computeVertexNormals();
// 方式2:手动定义法线(精准控制,复杂几何体)
const normals = new Float32Array([
0, 0, 1, // 顶点0:法线朝向Z轴正方向
0, 0, 1, // 顶点1:法线朝向Z轴正方向
0, 0, 1 // 顶点2:法线朝向Z轴正方向
]);
geometry.setAttribute('normal', new THREE.BufferAttribute(normals, 3));
4. UV 纹理坐标:绑定纹理贴图
UV 坐标(0-1范围)用于将2D图片贴到3D几何体上,每2个值为一组(U=横向,V=纵向):
// 定义UV坐标(每2个值为一组)
const uvs = new Float32Array([
0, 0, // 顶点0:贴图左下角
1, 0, // 顶点1:贴图右下角
0, 1 // 顶点2:贴图左上角
]);
geometry.setAttribute('uv', new THREE.BufferAttribute(uvs, 2));
// 加载纹理并绑定到材质
const textureLoader = new THREE.TextureLoader();
const texture = textureLoader.load('texture.jpg');
const material = new THREE.MeshBasicMaterial({ map: texture });
四、完整实战示例(自定义三角面+索引+顶点颜色)
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>BufferGeometry 完整示例</title>
<style>body { margin: 0; overflow: hidden; }</style>
</head>
<body>
<script type="module">
import * as THREE from 'https://threejsfundamentals.org/threejs/resources/threejs/r132/build/three.module.js'
import { OrbitControls } from "https://threejsfundamentals.org/threejs/resources/threejs/r132/examples/jsm/controls/OrbitControls.js"
// 1. 创建三大核心
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, window.innerWidth/window.innerHeight, 0.1, 1000);
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
camera.position.z = 2;
// 2. 自定义 BufferGeometry(带索引+顶点颜色)
const geometry = new THREE.BufferGeometry();
// 2.1 顶点坐标(4个唯一顶点,绘制矩形)
const vertices = new Float32Array([
-0.5, -0.5, 0, // 顶点0
0.5, -0.5, 0, // 顶点1
-0.5, 0.5, 0, // 顶点2
0.5, 0.5, 0 // 顶点3
]);
geometry.setAttribute('position', new THREE.BufferAttribute(vertices, 3));
// 2.2 顶点颜色(4个顶点对应4组颜色)
const colors = new Float32Array([
1, 0, 0, // 顶点0:红
0, 1, 0, // 顶点1:绿
0, 0, 1, // 顶点2:蓝
1, 1, 0 // 顶点3:黄
]);
geometry.setAttribute('color', new THREE.BufferAttribute(colors, 3));
// 2.3 索引缓冲区(指定三角面的顶点顺序)
const indices = new Uint16Array([
0, 1, 2, // 第一个三角面:0→1→2
1, 3, 2 // 第二个三角面:1→3→2
]);
geometry.setIndex(new THREE.BufferAttribute(indices, 1));
// 2.4 计算法线(可选,若用受光照材质则需要)
geometry.computeVertexNormals();
// 3. 创建材质(启用顶点颜色)
const material = new THREE.MeshBasicMaterial({
vertexColors: true, // 启用顶点颜色
side: THREE.DoubleSide,
wireframe: false
});
// 4. 创建网格并添加到场景
const mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);
// 5. 轨道控制器(交互)
const controls = new THREE.OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;
controls.dampingFactor = 0.05;
// 6. 动画循环
function animate() {
requestAnimationFrame(animate);
mesh.rotation.x += 0.01;
mesh.rotation.y += 0.01;
controls.update();
renderer.render(scene, camera);
}
animate();
// 7. 窗口适配
window.addEventListener('resize', () => {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
});
</script>
</body>
</html>
示例效果
- 场景中显示一个彩色矩形(由两个三角面组成);
- 矩形顶点分别为红、绿、蓝、黄,面内自动渐变;
- 支持鼠标旋转/缩放视角,矩形缓慢旋转。
五、注意事项与性能优化
1. 关键注意点
- 类型化数组必须正确:顶点坐标用
Float32Array,索引用Uint16Array/Uint32Array,不能混用; - itemSize 必须匹配:坐标=3,颜色=3/4,UV=2,索引=1,错误会导致几何体显示异常;
- 双面渲染:自定义几何体默认只渲染正面,需设置
side: THREE.DoubleSide避免背面不可见; - 内存释放:不再使用的几何体,必须调用
geometry.dispose()释放内存,避免内存泄漏。
2. 性能优化技巧
- 使用索引缓冲区:减少重复顶点,降低数据量;
- 控制顶点数量:复杂几何体按需分段,避免顶点过多;
- 静态数据复用:相同形状的几何体复用,无需重复创建;
- drawRange 局部渲染:只渲染需要显示的顶点范围,减少计算。
核心总结
- 核心地位:BufferGeometry 是 Three.js 所有几何体的底层核心,官方唯一推荐使用;
- 核心流程:类型化数组→BufferAttribute→setAttribute→绑定到渲染对象;
- 核心优化:索引缓冲区可减少顶点重复,是高性能自定义几何体的关键;
- 核心属性:
position(坐标)、color(颜色)、uv(纹理)、normal(法线)是最常用的属性; - 性能原则:用类型化数组、复用几何体、释放无用数据,最大化 GPU 渲染效率。