GLTF模型无法应用FabricJS画布纹理,BoxGeometry功能正常
我明白你在开发这款盒子设计工具时碰到的棘手问题——用BoxGeometry的时候每个面都能正常加载FabricJS的纹理,但换成GLTF模型后纹理就死活不显示,这确实挺让人挠头的。结合你贴出来的代码,我帮你梳理几个大概率的问题点和对应的解决办法:
1. 先搞定加载顺序和异步处理的坑
你现在的代码里,纹理创建和材质赋值的逻辑是在GLTF加载回调之外执行的,但store.outerMesh是要等GLTF加载完成后才会被赋值的。这就导致你遍历store.outerMesh.children的时候,要么外层模型还没初始化,要么模型还没加载完全,材质根本没机会赋到Mesh上。
另外,你用new Image()加载图片后直接传image.src给TextureLoader,图片加载是异步的,搞不好图片还没加载完纹理就创建了,那纹理自然是空的。
修复方案:
把纹理创建和材质赋值的逻辑移到GLTF加载完成的回调里,确保模型已经准备好,同时用TextureLoader直接加载DataURI来避免异步问题:
loader.load( Store.currentModel.models[store.activeIndex].path, (gltf) => { const outer = gltf.scene.clone() Store.core.scene.add(gltf.scene); Store.core.scene.add(outer); // 阴影设置保持不变 if (Config.shadow.enabled) { gltf.scene.children.forEach((mesh) => { mesh.traverse(child => { if (child.isMesh) { child.castShadow = true; child.receiveShadow = true; } }); }) } Store.mesh = gltf.scene; Store.outerMesh = outer; // 在这里处理纹理和材质,确保模型已加载完成 this.materialArr = []; const textureLoader = new THREE.TextureLoader(); // 遍历每个面的纹理数据 for (const key in this.canvasDataURI) { textureLoader.load(this.canvasDataURI[key], (texture) => { texture.wrapS = THREE.RepeatWrapping; texture.wrapT = THREE.RepeatWrapping; texture.flipY = false; texture.mapping = THREE.UVMapping; texture.anisotropy = store.core.renderer.capabilities.getMaxAnisotropy(); texture.needsUpdate = true; // 创建材质 const material = new THREE.MeshPhongMaterial({ map: texture, name: key, transparent: true, side: THREE.DoubleSide }); this.materialArr.push(material); // 立即给对应名称的Mesh赋值材质 this.assignMaterialToMesh(key, material); }); } }, // 进度和错误回调保持不变 (xhr) => { console.log((xhr.loaded / xhr.total * 100) + '% loaded'); }, (error) => { console.error('An error happened', error); } ); // 新增方法:根据名称给Mesh匹配材质 assignMaterialToMesh(meshName, material) { Store.outerMesh.traverse(child => { if (child.isMesh && child.name === meshName) { child.material = material; child.material.needsUpdate = true; child.geometry.uvsNeedUpdate = true; // 确保UV坐标更新 } }); }
2. 检查GLTF模型的UV坐标是否正常
BoxGeometry的UV是默认生成好的,每个面都对应纹理的完整区域,但GLTF模型就不一定了:
- 可能导出模型时没正确展开UV,导致纹理无法映射到面上
- 可能多个面共享同一个UV集,导致单独赋值材质时纹理显示混乱
验证与修复:
先打印所有Mesh的UV信息,看看是否正常:
Store.outerMesh.traverse(child => { if (child.isMesh) { console.log(`Mesh ${child.name} 的UV:`, child.geometry.attributes.uv); // 如果UV不存在,尝试重新生成(仅适用于简单盒子模型) if (!child.geometry.attributes.uv) { child.geometry.computeUVs(); child.geometry.uvsNeedUpdate = true; } } });
如果是模型导出的问题,记得导出GLTF时勾选「UV展开」选项,确保每个面有独立的UV通道。
3. 确认Mesh名称和材质名称完全匹配
你用_.find(this.materialArr, { name: child.name })来匹配材质,但如果GLTF模型里Mesh的名称和你constants.sides里的side名称对不上,就会找不到对应的材质,自然看不到纹理。
验证与修复:
先打印所有Mesh的名称,确认和你canvasDataURI里的key是否一致:
Store.outerMesh.traverse(child => { if (child.isMesh) { console.log('当前Mesh名称:', child.name); } });
如果名称不匹配,要么修改GLTF模型中Mesh的名称,要么调整你的constants.sides或canvasDataURI的key,确保两者完全对应。
4. 手动触发纹理和材质的更新
有时候即使纹理加载完成,也需要手动告诉Three.js要更新纹理和材质,否则渲染器可能不会读取最新的纹理数据:
- 纹理加载完成后设置
texture.needsUpdate = true; - 给Mesh赋值材质后,设置
child.material.needsUpdate = true;和child.geometry.uvsNeedUpdate = true; - 如果Fabric画布内容有更新,重新生成DataURI后,找到对应的纹理再设置一次
texture.needsUpdate = true;,不用重新创建材质
5. 检查外层盒子的可见性和层级
你克隆了GLTF场景作为外层盒子用于绘图,要确保外层盒子的visible属性是true,而且没有被原始模型遮挡。可以暂时隐藏原始模型测试一下:
gltf.scene.visible = false; // 测试时隐藏原始模型 Store.core.scene.add(outer); outer.visible = true;
按照上面的步骤排查和修复,应该能解决GLTF模型上FabricJS纹理不显示的问题。如果还有问题,可以看看控制台有没有报错,或者补充一下模型的导出细节。
内容的提问来源于stack exchange,提问作者Rohit Bhirud

