使用GLTFExporter导出OBJ时如何生成缺失的vt顶点纹理坐标?
解决缺失纹理坐标的OBJ模型生成UV并导出GLTF的问题
你遇到的核心问题是:仅仅给模型材质添加纹理并不会自动生成几何体层面的纹理坐标(UV)——Three.js渲染时可以临时处理,但GLTFExporter需要几何体中存在uv属性才能将纹理坐标写入导出的GLTF文件。下面是具体的解决方案:
步骤1:为模型几何体生成UV坐标
首先需要手动为每个Mesh的BufferGeometry添加uv属性。这里提供一个基于模型包围盒生成UV的通用函数,你可以根据模型的形状调整UV的计算逻辑(比如用X/Z轴而非X/Y轴):
function generateUVs(geometry) { // 计算模型的包围盒,用于归一化坐标 geometry.computeBoundingBox(); const boundingBox = geometry.boundingBox; const positionAttr = geometry.attributes.position; // 创建UV属性,每个顶点对应2个浮点数(u, v) const uvAttr = new THREE.Float32BufferAttribute(positionAttr.count * 2, 2); for (let i = 0; i < positionAttr.count; i++) { const x = positionAttr.getX(i); const y = positionAttr.getY(i); // 基于包围盒归一化X/Y坐标生成UV,适配ARCore OpenGL ES的纹理方向(v轴翻转) const u = (x - boundingBox.min.x) / (boundingBox.max.x - boundingBox.min.x); const v = 1 - ((y - boundingBox.min.y) / (boundingBox.max.y - boundingBox.min.y)); uvAttr.setXY(i, u, v); } // 将UV属性添加到几何体 geometry.setAttribute('uv', uvAttr); geometry.attributes.uv.needsUpdate = true; }
步骤2:修改OBJ加载逻辑,生成UV后再赋值材质
在你的loadBlockObject函数中,遍历Mesh的时候调用上面的生成函数,确保几何体拥有UV属性:
function loadBlockObject() { var texture = new THREE.TextureLoader().load('textures/uv_grid_opengl.jpg'); texture.wrapS = texture.wrapT = THREE.RepeatWrapping; texture.repeat.set(4, 4); // 注意:如果ARCore渲染出现纹理倒置,可以设置texture.flipY = false texture.flipY = false; var material = new THREE.MeshStandardMaterial({ map: texture }); var loader = new OBJLoader(); loader.load('models/obj/walt/block.obj', function (obj) { obj.traverse(function (child) { if (child instanceof THREE.Mesh) { // 先为几何体生成UV generateUVs(child.geometry); // 再赋值材质 child.material = material; } }); obj.scale.multiplyScalar(4); obj.position.set(400, 0, 0); scene1.add(obj); // 这里可以添加GLTF导出逻辑 exportToGLTF(obj); }); }
步骤3:正确配置GLTFExporter导出
添加导出函数,确保导出选项包含纹理和材质相关设置:
function exportToGLTF(model) { const exporter = new GLTFExporter(); exporter.parse( model, function(gltfData) { // 处理导出的GLTF数据,比如保存为文件 const jsonString = JSON.stringify(gltfData, null, 2); // 示例:下载导出的文件 const blob = new Blob([jsonString], { type: 'application/json' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = 'block_with_uv.gltf'; a.click(); URL.revokeObjectURL(url); }, function(error) { console.error('GLTF导出失败:', error); }, { embedImages: true, // 将纹理嵌入GLTF(否则需要单独保存纹理文件) onlyVisible: true, truncateDrawRange: true } ); }
关键说明
- ARCore的SceneView基于OpenGL ES,其纹理坐标的v轴方向与Three.js默认相反,所以生成UV时我们将v值设为
1 - 归一化值,或者直接设置texture.flipY = false来适配。 - 如果你需要更贴合模型形状的UV(比如球面、圆柱面),可以修改
generateUVs函数的坐标计算逻辑,比如针对不同拓扑的模型使用不同的UV映射方式。
内容的提问来源于stack exchange,提问作者Erazx
相关产品推荐
相关产品推荐

