Three.js如何正确配置纹理投影 解决挤出曲面纹理拉伸问题
Three.js沿路径挤出曲面纹理拉伸问题
效果截图
沿椭圆路径挤出制作3/4圆柱曲面时,加载纹理后没有均匀贴合曲面,出现明显拉伸变形。已知问题和纹理投影设置相关,但不清楚具体调整方式。
复现代码
class EllipseCurve3 extends THREE.Curve { ellipse = null constructor (ellipse) { super() this.ellipse = ellipse } getPoint(t, optionalTarget = new THREE.Vector3()) { const point = this.ellipse.getPoint(t, optionalTarget) return new THREE.Vector3( point.x, point.y, 0 ) } } // 初始化场景 const scene = new THREE.Scene(); var shape = new THREE.Shape(); shape.moveTo(0, 0); shape.moveTo(0, 1); shape.lineTo(50, 1); shape.moveTo(50, 0); shape.lineTo(0, 0); const arc = new THREE.EllipseCurve( 0, 0, // ax, aY 100, 100, // xRadius, yRadius 0, 1.5 * Math.PI, // aStartAngle, aEndAngle false, // aClockwise 0 // aRotation ), path = new EllipseCurve3(arc), geometry = new THREE.ExtrudeGeometry(shape, { bevelEnabled: false, extrudePath: path, steps: 50, depth: 5, amount: 20, material: 0, extrudeMaterial: 1 }); // 配置灯光 const ambientLight = new THREE.AmbientLight(0xffffff, 0.6); scene.add(ambientLight); const axesHelper = new THREE.AxesHelper(500); scene.add(axesHelper); const directionalLight = new THREE.DirectionalLight(0xffffff, 0.6); directionalLight.position.set(100, 200, 100); scene.add(directionalLight); // 配置相机 const width = 200; const height = width * (window.innerHeight / window.innerWidth); const camera = new THREE.OrthographicCamera( width / -2, width / 2, height / 2, height / -2, 0.1, 1000 ); camera.position.set(400, 400, 400); camera.lookAt(0, 0, 0); // 配置渲染器 const renderer = new THREE.WebGLRenderer({ antialias: true }); renderer.setSize(window.innerWidth, window.innerWidth); const controls = new THREE.OrbitControls(camera, renderer.domElement); renderer.render(scene, camera); document.body.appendChild(renderer.domElement); var textureLoader = new THREE.TextureLoader(); textureLoader.crossOrigin = true; const picture = 'https://threejs.org/examples/textures/uv_grid_opengl.jpg' textureLoader.load(picture, function(texture) { texture.wrapS = texture.wrapT = THREE.MirroredRepeatWrapping; texture.repeat.set(.01, .01); var material = new THREE.MeshBasicMaterial({ map: texture, needsUpdate: true, }); var mesh = new THREE.Mesh(geometry, material) mesh.rotation.x = Math.PI / 2 scene.add(mesh) }) function render() { renderer.render(scene, camera); controls.update(); requestAnimationFrame(render); } render();
解决方法
纹理拉伸的核心原因是ExtrudeGeometry默认生成UV时,沿挤出路径方向直接按参数t(0到1线性取值)映射,没有按实际弧长均匀分配UV,再加上原代码里2D轮廓绘制存在冗余moveTo操作生成了多余顶点,进一步打乱了UV对应关系。
两种修复方案按需选择:
- 方案一:手动重算几何体UV
挤出完成后遍历所有顶点,对圆柱面上的点,用顶点相对于圆心的角度值归一化作为U坐标,用顶点沿圆柱高度方向的位置归一化作为V坐标,完全匹配曲面弧度,不会出现拉伸。 - 方案二:直接用CylinderGeometry生成目标曲面(推荐)
要做标准3/4圆柱面不需要自定义挤出,Three.js自带的CylinderGeometry原生支持指定扫描角度,生成的UV默认按弧长均匀分布,不会有纹理拉伸问题,代码更简洁:// 参数依次为:顶面半径、底面半径、圆柱高度、径向分段数、高度分段数、是否开放面、起始角度、扫描角度 const geometry = new THREE.CylinderGeometry(100, 100, 50, 50, 1, true, 0, 1.5 * Math.PI)
原代码里的shape绘制存在冗余,连续绘制轮廓时按顺序连线闭合即可,不需要重复调用moveTo移动起点,避免生成多余顶点干扰UV计算。
内容的提问来源于stack exchange,提问作者许春风
相关产品推荐
相关产品推荐

