加载.mat文件顶点面法线时Three.js BufferGeometry渲染异常怎么办
Matlab模型导入Three.js网格破碎/法线异常修复
问题背景
将Matlab导出、存储于.mat结构体中的3D几何数据导入Three.js场景,数据包含顶点坐标、面索引、顶点法线、顶点色四类数组。数据加载完成后通过BufferGeometry手动设置属性构建网格,实现代码如下:
var keyName = keysArray[0]; meshGeometry = new THREE.BufferGeometry(); // 写入顶点坐标 var index = 0; var positions = new Float32Array(bfjson.data[keyName].vertices.length * 3); for (let i = 0; i < bfjson.data[keyName].vertices.length; i++) { positions[index++] = bfjson.data[keyName].vertices[i][0]; positions[index++] = bfjson.data[keyName].vertices[i][1]; positions[index++] = bfjson.data[keyName].vertices[i][2]; } meshGeometry.setAttribute('position', new THREE.BufferAttribute(positions, 3)); // 写入顶点法线 index = 0; var vectornormals = new Float32Array(bfjson.data[keyName].VertexNormals.length * 3); for (let i = 0; i < bfjson.data[keyName].VertexNormals.length; i++) { vectornormals[index++] = bfjson.data[keyName].VertexNormals[i][0]; vectornormals[index++] = bfjson.data[keyName].VertexNormals[i][1]; vectornormals[index++] = bfjson.data[keyName].VertexNormals[i][2]; } meshGeometry.setAttribute('normal', new THREE.BufferAttribute(vectornormals, 3)); // 写入面索引 index = 0; var faces = []; for (let i = 0; i < bfjson.data[keyName].faces.length; i++) { faces[index++] = bfjson.data[keyName].faces[i][0]; faces[index++] = bfjson.data[keyName].faces[i][1]; faces[index++] = bfjson.data[keyName].faces[i][2]; } meshGeometry.setIndex(faces); // 写入默认顶点色 const colors = []; for (let i = 0, n = meshGeometry.attributes.position.count; i < n; ++i) { colors.push(1, 1, 1); } meshGeometry.setAttribute('color', new THREE.Float32BufferAttribute(colors, 3)); // 写入自定义灌注属性 for (let i = 0; i < bfjson.data[keyName].CData.length; i++) { CData[i] = bfjson.data[keyName].CData[i]; } meshGeometry.setAttribute('perfusion', new THREE.Float32BufferAttribute(CData, 1)); mesh.geometry = meshGeometry; updateColors();
当前顶点着色逻辑可正常运行,但生成的网格存在面连接错误、法线错位,无法拼接为完整光滑曲面。
根因分析
问题出在索引起始值的规则不匹配:
- Matlab的面索引采用1-based计数,即数组中第一个顶点对应的索引值为
1 - Three.js的
BufferGeometry索引要求0-based计数,即第一个顶点对应的索引值为0
直接读取Matlab导出的面索引值,会导致所有面的顶点引用全部偏移1位,出现顶点错配,最终表现为网格破碎、法线和顶点无法对应、曲面断裂。
修复方案
- 修正面索引偏移
读取面索引时将每个值减1,对齐Three.js的索引规则,修改面索引遍历部分代码:for (let i = 0; i < bfjson.data[keyName].faces.length; i++) { // Matlab索引从1起始,减1转换为Three.js要求的0起始索引 faces[index++] = bfjson.data[keyName].faces[i][0] - 1; faces[index++] = bfjson.data[keyName].faces[i][1] - 1; faces[index++] = bfjson.data[keyName].faces[i][2] - 1; } - 优化索引数组类型(适配大模型)
如果模型顶点数超过65535,普通JS数组或Uint16Array无法存储超出范围的索引值,建议直接使用Uint32Array初始化面索引数组,避免索引溢出:var faces = new Uint32Array(bfjson.data[keyName].faces.length * 3); - 校验法线与面朝向
修正索引后如果仍存在局部发黑、光照异常的问题,可以调用Three.js内置方法重新计算顶点法线,排除原始导出法线的朝向问题:// 自动根据面拓扑重新计算平滑法线 meshGeometry.computeVertexNormals();
内容的提问来源于stack exchange,提问作者Elsamarie O2
相关产品推荐
相关产品推荐

