Three.js:能否为PlaneGeometry的特定面加载独立纹理?
解决方案:为PlaneGeometry的特定三角面单独映射纹理
你遇到的核心问题是默认的UV坐标是为整个PlaneGeometry生成的,所以即使给不同面分配了带纹理的材质,纹理也会基于全局UV被拉伸/重复到所有面上。要实现给特定三角面贴独立纹理,你需要为这些目标面单独设置UV坐标,配合多材质系统来完成。
下面是针对你的需求的可行实现方案,基于你提供的r87版本Three.js:
完整代码示例
<!DOCTYPE html> <html lang="en"> <head> <title>Plane Geometry Specific Face Texture</title> <meta charset="utf-8"> <style> body { font-family: Monospace; background-color: #f0f0f0; margin: 0px; overflow: hidden; } </style> </head> <body> <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/87/three.min.js"></script> <script> var camera, scene, renderer, geometry, materials, mesh; init(); animate(); function init() { scene = new THREE.Scene(); camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 1, 10000); camera.position.z = 500; scene.add(camera); // 几何参数设置 var thumbnail_width = 32; var thumbnail_height = 32; var width_segments = 10; var height_segments = 10; var plane_geometry_width = width_segments * thumbnail_width; var plane_geometry_height = height_segments * thumbnail_height; geometry = new THREE.PlaneGeometry(plane_geometry_width, plane_geometry_height, width_segments, height_segments); // 1. 加载纹理并配置 var texture = new THREE.TextureLoader().load("image_small/item_1.png"); texture.flipY = true; // 修正Three.js默认的Y轴翻转问题 texture.needsUpdate = true; // 2. 创建多材质:索引0是纹理材质,1、2是颜色材质 materials = [ new THREE.MeshBasicMaterial({ map: texture, side: THREE.DoubleSide }), new THREE.MeshBasicMaterial({ color: 0xff0000, side: THREE.DoubleSide }), new THREE.MeshBasicMaterial({ color: 0x00ff00, side: THREE.DoubleSide }) ]; // 3. 为目标面分配材质索引并修改UV var targetFaceIndices = [0, 1]; // 这里指定你要贴纹理的三角面索引(比如前2个三角面) var uvs = geometry.faceVertexUvs[0]; for (var i = 0; i < geometry.faces.length; i++) { if (targetFaceIndices.includes(i)) { // 给目标面分配纹理材质索引 geometry.faces[i].materialIndex = 0; // 修改该面的UV坐标,让纹理完整显示在这个三角面上 // 三角面的三个顶点对应纹理的(0,0), (0,1), (1,0) 或 (0,1), (1,1), (1,0),根据面的朝向调整 if (i % 2 === 0) { uvs[i][0].set(0, 0); uvs[i][1].set(0, 1); uvs[i][2].set(1, 0); } else { uvs[i][0].set(0, 1); uvs[i][1].set(1, 1); uvs[i][2].set(1, 0); } } else { // 其他面分配颜色材质索引(按你的需求循环) geometry.faces[i].materialIndex = (i % 3) === 0 ? 1 : 2; } } // 4. 标记几何体UV需要更新,按材质索引排序优化渲染 geometry.uvsNeedUpdate = true; geometry.sortFacesByMaterialIndex(); // 创建网格并添加到场景 mesh = new THREE.Mesh(geometry, materials); scene.add(mesh); // 渲染器设置 renderer = new THREE.WebGLRenderer(); renderer.setSize(window.innerWidth, window.innerHeight); document.body.appendChild(renderer.domElement); } function animate() { requestAnimationFrame(animate); render(); } function render() { renderer.render(scene, camera); } </script> </body> </html>
关键要点解释
UV坐标修改:
PlaneGeometry默认的UV是将整个纹理映射到整个平面,每个三角面的UV只对应纹理的一小部分。我们需要手动修改目标面的UV,让三个顶点的UV坐标覆盖(0,0)到(1,1)的范围,这样纹理就能完整显示在单个三角面上。材质索引分配:
明确给需要贴纹理的面设置materialIndex = 0(对应我们的纹理材质),其他面分配颜色材质的索引,确保渲染时每个面使用正确的材质。几何体更新标记:
修改UV后必须设置geometry.uvsNeedUpdate = true,让Three.js重新计算UV数据。sortFacesByMaterialIndex()可以让相同材质的面批量渲染,提升性能。
为什么之前的尝试没生效?
- UPDATE方案:仅设置纹理重复但未修改UV,纹理还是基于全局UV重复到整个平面,每个面依然显示重复后的纹理片段,无法实现单个面的独立纹理。
- UPDATE2方案:将所有面的
materialIndex设为0,导致所有面都使用纹理材质,同时修改了所有面的UV,不符合你只给特定2个三角面贴纹理的需求。
内容的提问来源于stack exchange,提问作者mrgloom
相关产品推荐
相关产品推荐

