Three.js中ExtrudeGeometry生成模型的纹理映射异常求助
解答
嘿,我来帮你搞定这个纹理问题!先看你的代码和效果,有两个关键问题需要处理:
1. 先修正代码里的基础错误
你在创建Shape的时候用了this.height,但在generate_box这个普通函数里,this并不指向函数的参数,这会导致Shape的顶点位置错误,甚至可能生成无效的几何体。直接改成传入的height参数就好:
const shape = new THREE.Shape(); shape.lineTo(0, height); // 把this.height改成height shape.lineTo(width, height); shape.lineTo(width, 0);
2. 解决ExtrudeGeometry的UV映射问题
这是核心问题:ExtrudeGeometry默认的UV生成逻辑是为复杂路径设计的,它会把纹理沿着挤出方向拉伸,导致长方体侧面的纹理变形。针对你的需求,有两种解决方案:
方案一:手动调整UV坐标(适合必须用ExtrudeGeometry的场景)
如果你的Shape以后可能不是简单矩形,需要保留ExtrudeGeometry,那我们可以手动遍历几何体的UV属性,为每个面计算正确的UV值:
let generate_box = (width, height, depth) => { let extrudeSettings = { depth: depth, bevelEnabled: false, } const shape = new THREE.Shape(); shape.lineTo(0, height); shape.lineTo(width, height); shape.lineTo(width, 0); const geometry = new THREE.ExtrudeGeometry(shape, extrudeSettings); // 获取UV和位置属性 const uvAttribute = geometry.attributes.uv; const positions = geometry.attributes.position; // 遍历每个顶点,重新计算UV for (let i = 0; i < uvAttribute.count; i++) { const x = positions.getX(i); const y = positions.getY(i); const z = positions.getZ(i); // 处理顶部/底部面(z坐标接近±depth/2) if (Math.abs(z - depth/2) < 0.001 || Math.abs(z + depth/2) < 0.001) { uvAttribute.setXY(i, x / width, y / height); } // 处理前后侧面(x坐标接近0或width) else if (Math.abs(x) < 0.001 || Math.abs(x - width) < 0.001) { uvAttribute.setXY(i, z / depth, y / height); } // 处理左右侧面(y坐标接近0或height) else if (Math.abs(y) < 0.001 || Math.abs(y - height) < 0.001) { uvAttribute.setXY(i, x / width, z / depth); } } // 标记UV属性需要更新 uvAttribute.needsUpdate = true; return geometry; };
另外,你还可以根据需要设置纹理的重复模式,让纹理在面上重复显示:
material.map.wrapS = THREE.RepeatWrapping; material.map.wrapT = THREE.RepeatWrapping; // 比如让纹理在x方向重复2次,y方向不重复 // material.map.repeat.set(2, 1);
方案二:改用BoxGeometry(更简单高效)
如果你的需求只是创建一个普通长方体,那完全没必要用ExtrudeGeometry!BoxGeometry是Three.js专门为长方体设计的几何体,默认就会生成正确的UV映射,代码也更简洁:
import wood from "./wood_texture.jpg" // 直接创建BoxGeometry,参数对应width, height, depth let box_geo = new THREE.BoxGeometry(0.5, 2.2, 0.35); let textureLoader = new THREE.TextureLoader(); let material = new THREE.MeshStandardMaterial({ color: "rgb(248, 247, 247)", map: textureLoader.load(wood) }); box_mesh = new THREE.Mesh(box_geo, material); scene.add(box_mesh);
总结
- 先修正
this.height的错误,确保几何体形态正确 - 如果是普通长方体,优先用BoxGeometry,省心又高效
- 如果必须用ExtrudeGeometry,手动调整UV坐标就能得到你想要的纹理效果
内容的提问来源于stack exchange,提问作者Reza Aalaei
相关产品推荐
相关产品推荐

