如何从GLTFLoader加载的glTF场景中获取顶点与三角面数组
从glTF加载场景提取顶点与三角面数组实现方法
GLTFLoader解析完成后返回的场景对象是Three.js的Group实例,所有模型网格以Mesh子节点的形式挂载在组下,网格的BufferGeometry属性里存储了完整的顶点、拓扑数据,直接遍历提取即可,不需要额外依赖。
核心数据说明
- 顶点坐标列表:存储在网格几何体的
attributes.position属性中,类型为Float32BufferAttribute,访问.array可得到原始Float32Array数组,数组每3个连续元素对应1个顶点的X、Y、Z三维坐标 - 三角面索引列表:分两种场景
- 索引几何体(绝大多数glTF模型采用):数据存储在几何体的
index属性中,访问.array可得到Uint16Array或Uint32Array数组,每3个连续元素对应1个三角面的三个顶点在顶点列表中的下标 - 非索引几何体:无独立索引数组,顶点列表按顺序每3个连续顶点组成1个三角面
- 索引几何体(绝大多数glTF模型采用):数据存储在几何体的
修改后完整代码
原代码存在Promise链式调用未返回值、加载完成后未遍历提取几何数据的问题,修正后代码如下:
return from( fetch(targetUrl.toString(), { method: 'GET', headers: { 'Content-Type': 'application/json' } }) .then(response => { // 补全return,保证外层Promise链能拿到解析后的数据 return response.arrayBuffer() }) .then(data => { return new Promise((resolve) => { const loader = new GLTFLoader(); // 如果使用了Draco/Meshopt压缩,在此处注册对应解码器扩展即可 loader.parse(data, "", (gltf) => { const allMeshes = []; const allVertexData = []; const allTriangleIndexData = []; // 遍历场景下所有节点,筛选出网格类型 gltf.scene.traverse((node) => { if (node.isMesh && node.geometry) { const geometry = node.geometry; // 提取顶点坐标数组 const positionAttr = geometry.getAttribute('position'); const vertexArray = positionAttr.array; // Float32Array类型,即需要的顶点列表 // 提取三角面索引数组 let triangleIndexArray = null; if (geometry.index) { triangleIndexArray = geometry.index.array; // Uint16Array/Uint32Array类型,即需要的三角面索引列表 } else { // 非索引几何体,生成顺序索引 triangleIndexArray = new Uint32Array(vertexArray.length / 3); for (let i = 0; i < triangleIndexArray.length; i++) { triangleIndexArray[i] = i; } } // 按需求存储数据 const meshData = { mesh: node, vertices: vertexArray, triangleIndices: triangleIndexArray }; allMeshes.push(meshData); allVertexData.push(vertexArray); allTriangleIndexData.push(triangleIndexArray); // 把网格加入场景 this.scene.add(node); } }); // 返回提取好的几何数据供后续pipe流程使用 resolve({ gltfScene: gltf.scene, meshes: allMeshes, allVertices: allVertexData, allTriangleIndices: allTriangleIndexData }); }) }) }) ).pipe( map((loadResult) => { // 此处可以直接拿到所有顶点、三角面数组 this.surfaceMeshes.set(surfaceMeshKey, loadResult); return loadResult; }) )
注意事项
- 如果glTF模型做了几何压缩(Draco、Meshopt等),需要先给GLTFLoader注册对应的解码扩展,否则提取到的几何数据为空
- 拿到的TypedArray是几何体内部引用的原始数据,如果需要修改请先做克隆,避免直接修改原始缓冲导致渲染异常
- 如果模型带蒙皮、变形器,上述方法拿到的是初始绑定姿态的顶点数据,如需获取动画运行中的实时顶点位置,需要用
SkinnedMesh的boneTransform方法逐顶点计算,或调用几何体对应更新方法后再读取
内容的提问来源于stack exchange,提问作者user13856337
相关产品推荐
相关产品推荐

