如何为Three.js扁平圆柱体单面分配置换/法线贴图
如何让Three.js扁平圆柱体仅单侧显示displacementMap和normalMap
嘿,这个问题我之前也碰到过!其实核心原因是Three.js的CylinderGeometry默认会给上下两个底面都分配完整的UV坐标,所以贴图会同时显示在两侧。这里有几个实用的解决方案,你可以根据需求选:
方法一:修改几何体UV坐标
直接调整其中一个底面的UV数据,让它不采样贴图(比如把所有顶点UV设为同一个点,或者超出贴图范围让它显示透明/纯色)。
// 创建扁平圆柱体几何体 const geometry = new THREE.CylinderGeometry(50, 50, 2, 128); // 获取UV属性 const uvAttribute = geometry.getAttribute('uv'); const radiusSegments = 128; // 假设最后radiusSegments个UV对应底部底面的顶点,把它们都设为(0,0) // 这样底部会采样贴图的左上角像素,如果你的贴图该位置是透明/纯色,就不会显示贴图效果 for (let i = uvAttribute.count - radiusSegments; i < uvAttribute.count; i++) { uvAttribute.setXY(i, 0, 0); } // 标记UV属性需要更新 uvAttribute.needsUpdate = true; // 创建带贴图的材质 const material = new THREE.MeshStandardMaterial({ displacementMap: yourDisplacementMap, normalMap: yourNormalMap, // 可选:如果想让底面显示纯色,可以配合alphaMap或者调整贴图的透明通道 }); const coinMesh = new THREE.Mesh(geometry, material);
方法二:使用多材质分别控制面
给几何体的不同面分配不同的材质索引,一个带贴图,一个用纯色材质,这样能精准控制每个面的显示效果。
const geometry = new THREE.CylinderGeometry(50, 50, 2, 128); const radiusSegments = 128; const sideFaceCount = 2 * radiusSegments; // 侧面的三角面数量 const topFaceCount = radiusSegments; // 顶部底面的三角面数量 // 给每个面设置材质索引 for (let i = 0; i < geometry.faces.length; i++) { // 侧面和顶部用带贴图的材质(索引0),底部用纯色材质(索引1) if (i >= sideFaceCount + topFaceCount) { geometry.faces[i].materialIndex = 1; } else { geometry.faces[i].materialIndex = 0; } } // 创建两种材质 const texturedMaterial = new THREE.MeshStandardMaterial({ displacementMap: yourDisplacementMap, normalMap: yourNormalMap, // 其他材质属性,比如color、roughness等 }); const plainMaterial = new THREE.MeshStandardMaterial({ color: 0xaaaaaa, // 硬币另一面的纯色 roughness: 0.5, metalness: 0.3 }); // 传入材质数组创建网格 const coinMesh = new THREE.Mesh(geometry, [texturedMaterial, plainMaterial]);
方法三:组合两个独立网格(更直观)
如果不想折腾几何体的UV或材质索引,也可以直接用两个平面+一个细圆柱体侧面组合成硬币:
- 一个
PlaneGeometry作为带贴图的正面 - 另一个
PlaneGeometry作为纯色的背面 - 一个细的
CylinderGeometry(只保留侧面)作为硬币的边缘
这种方法虽然需要额外管理几个网格,但逻辑更简单,适合快速实现。
内容的提问来源于stack exchange,提问作者Nulik
相关产品推荐
相关产品推荐

