几何体就是一堆顶点数据
在 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把 indices.push(a, c, b, b, c, d) 改成 push(a, b, c, b, d, c),翻转环绕顺序,再把材质改回 FrontSide,看看哪一面消失了。
- 2删掉 normal 属性的计算,改为每帧调用 geometry.computeVertexNormals(),比较效果和性能。
- 3把 segments 调到 2,开启 wireframe,数一数三角形数量是否和状态栏一致。
- 4修改 height 函数,做一个从中心向外扩散的涟漪:sin(距离 * k - t)。
还没有输出。console.log 的内容会显示在这里。
运行中