如何使用ThreeJS将FabricJS画布作为OBJ模型的纹理
问题描述
我尝试通过ThreeJS将添加了SVG的FabricJS作为3D模型的纹理,目前3D模型直接使用SVG作为纹理时运行正常,但如果直接映射FabricJS为纹理,原有SVG内容就会消失,因此我认为最好先将SVG放置到FabricJS画布上。
原有设置纹理代码
function set_materials(response) { var baseSvg = document.getElementById("svgContainer").querySelector("svg"); var baseSvgData = (new XMLSerializer()).serializeToString(baseSvg); $('#svgPathContainer').empty(); $('#svgTextContainer').empty(); $('#svgPathContainer').append(baseSvgData).html(); $('#svgTextContainer').append(baseSvgData).html(); var texts = $('#svgPathContainer text'); for (var i = 0; i < texts.length; i++) { $(texts[i]).remove(); } var paths = $('#svgTextContainer path'); for (var i = 0; i < paths.length; i++) { $(paths[i]).remove(); } var svg = document.getElementById("svgPathContainer").querySelector("svg"); var svgData = (new XMLSerializer()).serializeToString(svg); canvas.width = $(svg).width(); canvas.height = $(svg).height(); var ctx = canvas.getContext("2d"); var img = document.createElement("img"); var material; img.setAttribute("src", "data:image/svg+xml;base64," + window.btoa(unescape(encodeURIComponent(svgData)))); img.onload = function () { ctx.drawImage(img, 0, 0); var oImg = document.createElement("img"); oImg.width = "100px"; oImg.height = "100px"; oImg.setAttribute("src", 'assets/' + gender + '/cat' + category + '/texture.png'); oImg.onload = function () { ctx.globalAlpha = 0.4; ctx.scale(0.3, 0.3); var pattern = ctx.createPattern(oImg, 'repeat'); ctx.fillStyle = pattern; ctx.fillRect(0, 0, canvas.width * 3.33, canvas.height * 3.33); ctx.globalAlpha = 1; ctx.scale(3.33, 3.33); var svgText = document.getElementById("svgTextContainer").querySelector("svg"); var svgTextData = (new XMLSerializer()).serializeToString(svgText); var imgT = document.createElement("img"); imgT.setAttribute("src", "data:image/svg+xml;base64," + window.btoa(unescape(encodeURIComponent(svgTextData)))); imgT.onload = function () { ctx.drawImage(imgT, 0, 0); texture.anisotropy = renderer.capabilities.getMaxAnisotropy(); map = texture; textureMaterial = new THREE.MeshPhongMaterial({ map: map }); texture.needsUpdate = true; load_materials(); load_styles(); response(true); } } }; }
尝试绑定FabricJS画布纹理的代码
function loadObj() { var loader = new THREE.OBJLoader2(manager); loader.load('assets/men/cat1/model1.obj', function (data) { if (object != null) { scene.remove(object); } object = null; object = data.detail.loaderRootNode; materials = []; object.traverse(function (child) { if (child.isMesh) { child.material.map = canvasTexture; }; }); object.children[0].material.map = canvasTexture; var scale = height / 5; object.scale.set(scale, scale, scale); object.position.set(0, -scale * 1.25, 0); object.rotation.set(0, Math.PI / 2, 0); object.receiveShadow = true; object.castShadow = true; scene.add(object); }); }

当前已检查确认node.material.map与canvasTexture的值一致,但模型显示效果异常,问题原因及解决方法如下:
问题原因与解决方案
核心原因
- FabricJS渲染异步:FabricJS的画布内容渲染是异步操作,你在SVG还没完全绘制到Fabric画布上时就创建了ThreeJS纹理,导致纹理读取了空画布内容。
- 更新标记缺失:替换纹理后没有给材质和纹理设置更新标记,ThreeJS不会主动拉取最新的画布内容到GPU。
- 纹理参数不匹配:如果Fabric画布尺寸不是2的幂,默认的Mipmap采样会失效,导致纹理显示异常。
- 透明通道冲突:Fabric画布默认背景透明,和模型材质的透明度设置冲突会导致内容看起来消失。
修复步骤
- 确保SVG完全渲染到Fabric画布后再创建纹理
// 正确加载SVG到Fabric画布的写法 fabric.loadSVGFromString(svgData, function(objects, options) { const svgGroup = fabric.util.groupSVGElements(objects, options); fabricCanvas.add(svgGroup).renderAll(); // 渲染完成后再初始化纹理 initCanvasTexture(); });
- 正确创建纹理并设置更新标记
function initCanvasTexture() { const canvasTexture = new THREE.CanvasTexture(fabricCanvas.getElement()); canvasTexture.needsUpdate = true; canvasTexture.anisotropy = renderer.capabilities.getMaxAnisotropy(); // 非2的幂尺寸画布需要额外设置过滤和包裹模式 canvasTexture.wrapS = canvasTexture.wrapT = THREE.ClampToEdgeWrapping; canvasTexture.minFilter = THREE.LinearFilter; return canvasTexture; } // 后续每次修改Fabric画布内容后,都要主动标记纹理更新 canvasTexture.needsUpdate = true;
- 修正材质绑定逻辑,避免透明冲突
function loadObj() { var loader = new THREE.OBJLoader2(manager); loader.load('assets/men/cat1/model1.obj', function (data) { if (object != null) { scene.remove(object); } object = data.detail.loaderRootNode; const canvasTexture = initCanvasTexture(); object.traverse(function (child) { if (child.isMesh) { child.material = new THREE.MeshPhongMaterial({ map: canvasTexture, side: THREE.DoubleSide, transparent: false // 不需要透明时关闭,避免通道干扰 }); child.material.needsUpdate = true; }; }); // 剩余缩放、位置、添加到场景的逻辑保持不变 var scale = height / 5; object.scale.set(scale, scale, scale); object.position.set(0, -scale * 1.25, 0); object.rotation.set(0, Math.PI / 2, 0); object.receiveShadow = true; object.castShadow = true; scene.add(object); }); }
内容的提问来源于stack exchange,提问作者naoval luthfi
相关产品推荐
相关产品推荐

