跳到主要内容
3D Playground
Three.js 核心第 2 / 13 节

几何体与 BufferGeometry

入门WebGL

几何体就是一堆顶点数据

在 three.js 里,所有形状最终都是 BufferGeometry:一组顶点属性(attribute),每个属性是一段连续的类型化数组(Float32Array),会被原样上传到 GPU 显存。BoxGeometry、SphereGeometry、TorusKnotGeometry 这些内置几何体,只是帮你把这些数组算好了而已。

三个最常用的属性

  • position:每个顶点的 xyz 坐标,itemSize = 3。
  • normal:每个顶点的法线(垂直于表面的单位向量),光照计算全靠它。法线错了,物体就会“发黑”或光照怪异。
  • uv:每个顶点的纹理坐标 (u, v),范围通常是 0–1,itemSize = 2,决定贴图怎么铺在表面上。

用 geometry.setAttribute("position", new THREE.BufferAttribute(array, 3)) 把数组交给几何体。

索引:让顶点被共享

网格由三角形组成。如果不用索引,每个三角形都要存 3 个独立顶点,相邻三角形的公共顶点会被重复存储。调用 geometry.setIndex([...]) 后,三角形只记录“用第几号顶点”,同一个顶点可以被多个三角形复用。示例中的曲面每个格子由两个三角形组成:

js
const a = j * (n + 1) + i, b = a + 1, c = a + (n + 1), d = c + 1;
indices.push(a, c, b,  b, c, d);

顶点的环绕顺序很重要:从正面看是逆时针的三角形才是正面(front face),背面默认会被剔除。示例材质用了 DoubleSide,所以从下面看也能看到。

动态更新

想让几何体动起来,可以每帧直接改写数组里的数字,然后设置 attribute.needsUpdate = true,three.js 就会把新数据重新上传。频繁更新的属性最好调用 setUsage(THREE.DynamicDrawUsage) 提示驱动。示例里的法线是根据波函数的导数解析计算的;更省事的做法是 geometry.computeVertexNormals(),它会根据相邻三角形自动估算法线。

打开 showNormals,黄色短线就是 normal 属性;把 segments 调小,可以清楚看到三角形是怎么拼起来的。

核心 API

THREE.BufferGeometryTHREE.BufferAttributegeometry.setAttributegeometry.setIndexattribute.needsUpdateTHREE.DynamicDrawUsageTHREE.TorusKnotGeometryVertexNormalsHelper

动手试试

  1. 1把 indices.push(a, c, b, b, c, d) 改成 push(a, b, c, b, d, c),翻转环绕顺序,再把材质改回 FrontSide,看看哪一面消失了。
  2. 2删掉 normal 属性的计算,改为每帧调用 geometry.computeVertexNormals(),比较效果和性能。
  3. 3把 segments 调到 2,开启 wireframe,数一数三角形数量是否和状态栏一致。
  4. 4修改 height 函数,做一个从中心向外扩散的涟漪:sin(距离 * k - t)。
运行中