🎬 开场白
如果说上一章的烟花粒子让 Three.js 的舞台热闹非凡,那么你有没有想过:这些绚丽的粒子,本质上是由点、线、面拼接而成的?
要真正搭建出属于自己的三维世界,我们得从这些最基础的“几何积木”——几何体(Geometry)讲起。
传统几何体 vs BufferGeometry
三维物体的组成
点 (Vertex) ➝ 线 (Line) ➝ 面 (Face) ➝ 体 (Mesh)
在 Three.js 中,一个三维物体并不是直接“画出来”的,而是逐层由基础元素组合而成的:
点(Vertex)
最基本的单位,用三维坐标 (x, y, z) 表示位置。
多个点可以作为构建几何体的基石。
线(Edge/Line)
由两个顶点相连而成。
多条线可以勾勒出物体的轮廓。
面(Face)
由三个或更多顶点首尾相连形成的平面区域。
在 Three.js 中,最常见的是三角形面(Triangle)。
面可以填充颜色、贴图、并参与光照计算。
体(Mesh)
由多个面组合而成的完整三维模型。
在 Three.js 中通常用 Mesh 表示:Mesh = 几何体 (Geometry/BufferGeometry) + 材质 (Material)。
👉 这样,点 → 线 → 面 → 体 的层次关系,就构成了我们在 Three.js 中看到的三维世界。
在 Three.js 的早期,Geometry 是主力军,就像普通数组一样,使用简单直观,但性能一般。
而现代的 Three.js 更推荐使用 BufferGeometry,它像 TypedArray 一样,直接和 GPU 打交道,能存放更多顶点、效率更高。
这一章,我们就来搞清楚 Geometry 和 BufferGeometry 的区别,以及如何用它们搭建三维模型的基石。
| 特性 | Geometry | BufferGeometry |
|---|---|---|
| 数据结构 | 普通数组存储顶点 | TypedArray 存储顶点数据 |
| 使用难度 | 简单直观,学习成本低 | 稍复杂,需要理解缓冲区概念 |
| 性能表现 | CPU 计算为主,效率一般 | GPU 加速,性能更高 |
| 灵活性 | 方便修改顶点和面 | 修改相对繁琐,需要操作缓冲区 |
| Three.js 支持 | 旧版本主力,已逐渐弃用 | 官方推荐,未来标准 |
早期的 Three.js 提供了 THREE.Geometry,可以直接往里加顶点和面,非常直观:
// 创建一个几何体
const geometry = new THREE.Geometry();
// 添加顶点
geometry.vertices.push(
new THREE.Vector3(0, 0, 0), // 顶点0
new THREE.Vector3(1, 0, 0), // 顶点1
new THREE.Vector3(0, 1, 0) // 顶点2
);
// 添加一个面(由三个顶点组成)
geometry.faces.push(new THREE.Face3(0, 1, 2));
// 用材质组合成网格
const material = new THREE.MeshBasicMaterial({ color: 0x3399ff, wireframe: true });
const mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);
BufferGeometry 直接用底层的 Typed Array 存储数据,更高效。
// 用类型化数组定义顶点坐标 (3个点,每点3个值)
const vertices = new Float32Array([
0, 0, 0, // 顶点0
1, 0, 0, // 顶点1
0, 1, 0 // 顶点2
]);
// 创建几何体
const geometry = new THREE.BufferGeometry();
// 把顶点数据绑定到 position 属性
geometry.setAttribute('position', new THREE.BufferAttribute(vertices, 3));
// 创建材质 & 网格
const material = new THREE.MeshBasicMaterial({ color: 0xff9933, wireframe: true });
const mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);
JavaScript Typed Array(类型化数组)大全
JavaScript 的 Typed Array 是一类数组,用来在 连续内存块里存储特定类型的数据,特别适合 WebGL / GPU / 高性能计算 使用。相比普通数组,Typed Array 不能存普通对象,只能存数字,而且类型固定。
整数类型
| 类型 | 字节大小 | 取值范围 | 是否有符号 | 通俗理解 |
|---|---|---|---|---|
Int8Array | 1 byte | -128 ~ 127 | ✅ 有符号 | 小整数,1字节一个 |
Uint8Array | 1 byte | 0 ~ 255 | ❌ 无符号 | 常用存颜色(0~255) |
Uint8ClampedArray | 1 byte | 0 ~ 255 | ❌ 无符号 | 像素颜色专用,自动 clamp 超出值 |
Int16Array | 2 bytes | -32768 ~ 32767 | ✅ 有符号 | 中等整数,2字节一个 |
Uint16Array | 2 bytes | 0 ~ 65535 | ❌ 无符号 | 中等整数,2字节一个 |
Int32Array | 4 bytes | -2,147,483,648 ~ 2,147,483,647 | ✅ 有符号 | 大整数,4字节一个 |
Uint32Array | 4 bytes | 0 ~ 4,294,967,295 | ❌ 无符号 | 大整数,4字节一个 |
浮点类型
| 类型 | 字节大小 | 取值范围 | 通俗理解 |
|---|---|---|---|
Float32Array | 4 bytes | ±3.4×10³⁸(约7位精度) | GPU / WebGL 顶点坐标常用 |
Float64Array | 8 bytes | ±1.8×10³⁰⁸(约16位精度) | 高精度科学计算 |
BigInt 类型(ES2020 引入)
| 类型 | 字节大小 | 取值范围 | 通俗理解 |
|---|---|---|---|
BigInt64Array | 8 bytes | ±9,223,372,036,854,775,807 | 超大整数,JavaScript Number 精度不够用 |
BigUint64Array | 8 bytes | 0 ~ 18,446,744,073,709,551,615 | 超大无符号整数 |
创建与使用
// 方法 1:指定长度
// 创建长度为3的 Float32Array
const arr = new Float32Array(3);
console.log(arr); // Float32Array [0, 0, 0]
// 方法 2:用普通数组初始化
const arr = new Float32Array([1.0, 2.0, 3.0]);
console.log(arr); // Float32Array [1, 2, 3]
// 方法 3:从已有 ArrayBuffer 创建
const buffer = new ArrayBuffer(12); // 12字节
const arr = new Float32Array(buffer);
BufferGeometry 概览
常用属性
| 属性 | 类型 | 说明 |
|---|---|---|
attributes.position | Float32BufferAttribute | 顶点坐标,每 3 个数为一个顶点 (x, y, z) |
attributes.normal | Float32BufferAttribute | 法向量,用于光照计算,每 3 个数为一个向量 |
attributes.color | Float32BufferAttribute | 顶点颜色,每 3 个数为 RGB |
attributes.uv | Float32BufferAttribute | UV 贴图坐标,每 2 个数为一个纹理点 (u, v) |
index | Uint16Array / Uint32Array | 索引数组,用于复用顶点,减少重复存储 |
drawRange | Object | 控制渲染的起始点和顶点数量 |
groups | Array | 支持分组渲染不同材质 |
boundingBox / boundingSphere | Box3 / Sphere | 几何体边界信息,用于碰撞或裁剪 |
使用方法示例
// 创建一个 BufferGeometry
const geometry = new THREE.BufferGeometry();
// 定义顶点数据 (3个顶点构成一个三角形)
const vertices = new Float32Array([
0, 0, 0, // 顶点0
1, 0, 0, // 顶点1
0, 1, 0 // 顶点2
]);
// 将顶点数据绑定到 position 属性
geometry.setAttribute('position', new THREE.BufferAttribute(vertices, 3));
// 可选:定义索引,复用顶点
const indices = new Uint16Array([0, 1, 2]);
geometry.setIndex(new THREE.BufferAttribute(indices, 1));
// 创建材质和网格
const material = new THREE.MeshBasicMaterial({ color: 0xff0000 });
const mesh = new THREE.Mesh(geometry, material);
// 添加到场景
scene.add(mesh);
内置几何体分类
在 Three.js 中,几何体(Geometry / BufferGeometry)是创建三维模型的基础。为了方便快速构建常见形状,Three.js 提供了一系列内置几何体(Built-in Geometries),我们可以直接使用,无需手动定义顶点和面。
为什么要有内置几何体?
- 快速创建常见形状:比如立方体、球体、圆柱、平面等,避免重复手动定义顶点。
- 节省计算和代码量:内置几何体内部已经封装好了顶点、法线、UV 等数据。
- 便于统一操作和渲染:所有内置几何体都是 BufferGeometry 的实例,可以直接挂载材质(Material)进行渲染。
- 可拓展性:可以在内置几何体的基础上修改顶点或生成自定义几何体。
基础体
- BoxGeometry:立方体/长方体,你的积木砖头。
- SphereGeometry:球体,地球、篮球都可以。
- PlaneGeometry:平面,地板、墙壁的最佳选择。
- CircleGeometry:圆形,简单又高效的圆盘。
- ConeGeometry:圆锥,圣诞树、冰激凌造型都能来。
- CylinderGeometry:圆柱,水桶、火箭筒都靠它。
环形体
- TorusGeometry:甜甜圈,带洞洞的圆环。
- TorusKnotGeometry:神秘结,数学和艺术的完美结合。
- RingGeometry:圆环平面版,像戒指或光环。
正多面体
- TetrahedronGeometry:四面体,顶点狂欢的小家伙。
- OctahedronGeometry:八面体,钻石的灵感源泉。
- DodecahedronGeometry:十二面体,魔法骰子!
- IcosahedronGeometry:二十面体,适合模拟球形效果。
- PolyhedronGeometry:自定义多面体,随心所欲。
路径类
- TubeGeometry:沿着路径生成管道,比如火箭轨道或蛇形灯。
- LatheGeometry:旋转体,通过旋转 2D 轮廓得到三维模型,像车轮或花瓶。
自由绘制
- ShapeGeometry:用 Shape 绘制自定义平面。
- ExtrudeGeometry:拉伸 Shape,生成三维立体,比如 3D 字体或装饰物。
辅助几何
- EdgesGeometry:显示模型边缘,调试或做线框风格。
- WireframeGeometry:整个模型线框化,带点科技感。
🌟 结尾
好了,经过这一章的疯狂堆砌,我们的三维积木世界已经初具规模 🧱。但是,不管是立方体、球体,还是那条神秘的管道,如果没有尺寸和单位的概念,它们可能会大得像高楼,或者小到只有蚂蚁能看到 😅。
所以,在下一章里,我们要把这些三维积木放到“现实世界”的尺子上,聊聊 Three.js 的单位体系、如何设置几何体尺寸,以及如何用比例和缩放让你的模型既美观又合理。毕竟,再炫的模型,如果大小不对,看起来就像拿放大镜看蚂蚁一样尴尬 😂。