如何在Three.js中对无填充的SVG路径进行挤出建模?
问题:如何对无填充SVG路径的边框进行挤出建模?
我有如下SVG文件:
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:svgjs="http://svgjs.dev/svgjs" width="900" height="800"> <path d="M 650,750 L 50,750 L 100,350 L 850,50 L 850,550 L 650,750" fill="none" stroke="black" stroke-width="3" /> </svg>
当我尝试用ExtrudeGeometry渲染它时,得到了带有填充的效果,尽管SVG路径已设置fill="none",我期望仅渲染边框,但实际并非如此。
我使用的加载代码如下:
const svgResult = loader.parse(svgTxt); svgResult.paths.forEach((path) => { const shapes = SVGLoader.createShapes(path); shapes.forEach((shape) => { const geom = new THREE.ExtrudeGeometry(shape, { bevelEnabled: true, bevelSize: 14, bevelThickness: 5, bevelSegments: 15, depth: 20, }); const mesh = new THREE.Mesh( geom, new THREE.MeshPhysicalMaterial({ color: "white", }) ); // 省略添加到场景等代码 }); });
解决方案
核心问题在于SVGLoader.createShapes()会把闭合的SVG路径直接转换成用于填充的Shape对象,完全忽略fill="none"属性。要实现边框的挤出建模,需要把路径当作线条轮廓处理,而不是填充区域,有两种可行方法:
方法一:使用LineGeometry实现线条立体效果(适合简单线条)
如果不需要复杂的倒角,可直接用Line2系列API创建带宽度的线条,再通过额外处理实现厚度:
const svgResult = loader.parse(svgTxt); svgResult.paths.forEach((path) => { // 提取SVG路径的顶点,转换为Three.js坐标系(y轴反向) const points = path.getPoints().map(p => new THREE.Vector3(p.x, -p.y, 0)); // 创建LineGeometry并设置顶点 const lineGeometry = new THREE.LineGeometry(); lineGeometry.setPositions(points.flatMap(v => [v.x, v.y, v.z])); // 创建LineMaterial,设置线条宽度 const lineMaterial = new THREE.LineMaterial({ color: 0xffffff, linewidth: 3, // 对应SVG的stroke-width }); lineMaterial.resolution.set(window.innerWidth, window.innerHeight); // 必须设置分辨率以保证宽度正确 // 创建Line2对象并添加到场景 const lineMesh = new THREE.Line2(lineGeometry, lineMaterial); scene.add(lineMesh); // 若需要挤出厚度,可复制LineGeometry并沿z轴偏移,手动连接成面(适合简单场景) });
方法二:将描边转为闭合Shape后挤出(支持倒角等参数)
这种方法会把SVG的线条描边转换成闭合的轮廓Shape,再用ExtrudeGeometry挤出,完美适配你原有的倒角配置:
const svgResult = loader.parse(svgTxt); svgResult.paths.forEach((path) => { // 获取SVG路径的描边宽度 const strokeWidth = path.userData.style.strokeWidth || 3; // 将SVG路径转换为Three.js的Path对象(注意y轴反向) const threePath = new THREE.Path(); const svgPoints = path.getPoints(); if (svgPoints.length > 0) { threePath.moveTo(svgPoints[0].x, -svgPoints[0].y); svgPoints.slice(1).forEach(p => threePath.lineTo(p.x, -p.y)); threePath.closePath(); } // 生成描边的闭合轮廓Shape数组 const strokeShapes = threePath.getStrokeShapes(strokeWidth); // 对每个轮廓Shape执行挤出 strokeShapes.forEach((shape) => { const geom = new THREE.ExtrudeGeometry(shape, { bevelEnabled: true, bevelSize: 14, bevelThickness: 5, bevelSegments: 15, depth: 20, }); const mesh = new THREE.Mesh( geom, new THREE.MeshPhysicalMaterial({ color: "white" }) ); scene.add(mesh); }); });
说明:Path.getStrokeShapes()方法会根据线条宽度生成内外两条闭合路径,组合成空心的轮廓Shape,挤出后就是你需要的边框立体效果,同时支持原代码中的倒角参数。
内容的提问来源于stack exchange,提问作者Mat
相关产品推荐
相关产品推荐

