You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

THREE.js单个Mesh多UV/多纹理映射实现问题咨询

解决Three.js中2×2 UV范围的多纹理映射问题

我来帮你搞定这个多纹理映射的问题——你的OBJ模型UV坐标是0到2的范围,四个象限分别对应四张纹理,现在只需要把每个UV区间的面分配给对应的材质,再把UV归一到0-1的标准范围,就能让所有网格都正确显示啦。

下面是具体的实现步骤,分两种几何体类型(旧版Geometry和新版BufferGeometry)来讲解:

第一步:加载所有纹理并配置

首先我们要加载四张目标纹理,同时设置纹理的包裹方式为ClampToEdgeWrapping,避免UV超出范围时出现重复或拉伸的问题:

// 初始化纹理加载器
const textureLoader = new THREE.TextureLoader();

// 加载四张纹理
const textures = {
  u1v1: textureLoader.load('tex_u1_v1.png'),
  u1v2: textureLoader.load('tex_u1_v2.png'),
  u2v1: textureLoader.load('tex_u2_v1.png'),
  u2v2: textureLoader.load('tex_u2_v2.png')
};

// 配置所有纹理的包裹规则,防止UV溢出导致的异常显示
Object.values(textures).forEach(texture => {
  texture.wrapS = THREE.ClampToEdgeWrapping;
  texture.wrapT = THREE.ClampToEdgeWrapping;
  texture.needsUpdate = true;
});

第二步:处理几何体并按UV分组

接下来需要遍历几何体的所有面,根据UV坐标的归属区间给面分组,同时把每个组的UV调整到0-1的标准范围(因为纹理本身的UV是0-1)。

方式1:针对旧版THREE.Geometry

如果你的几何体是旧版的Geometry类型,可以这样处理:

const geometry = object.geometry;
geometry.clearGroups(); // 清除现有分组

// 辅助函数:调整UV到0-1范围
const adjustUV = (faceUVs, uOffset, vOffset) => {
  faceUVs.forEach(uv => {
    uv.x -= uOffset;
    uv.y -= vOffset;
  });
};

// 遍历每个面,判断归属的纹理分组
for (let i = 0; i < geometry.faces.length; i++) {
  const faceUVs = geometry.faceVertexUvs[0][i];
  // 取三个顶点UV的平均值来判断归属区间(避免单个顶点UV异常影响分组)
  const avgU = (faceUVs[0].x + faceUVs[1].x + faceUVs[2].x) / 3;
  const avgV = (faceUVs[0].y + faceUVs[1].y + faceUVs[2].y) / 3;

  let groupIndex = 0;
  let uOffset = 0, vOffset = 0;

  // 根据平均UV判断所属纹理区域
  if (avgU < 1 && avgV < 1) {
    groupIndex = 0; // 对应tex_u1_v1.png
  } else if (avgU < 1 && avgV >= 1) {
    groupIndex = 1; // 对应tex_u1_v2.png
    vOffset = 1;
  } else if (avgU >= 1 && avgV < 1) {
    groupIndex = 2; // 对应tex_u2_v1.png
    uOffset = 1;
  } else {
    groupIndex = 3; // 对应tex_u2_v2.png
    uOffset = 1;
    vOffset = 1;
  }

  // 调整当前面的UV到0-1范围
  adjustUV(faceUVs, uOffset, vOffset);
  // 给当前面添加分组
  geometry.addGroup(i, 1, groupIndex);
}

// 标记几何体需要更新
geometry.uvsNeedUpdate = true;
geometry.groupsNeedUpdate = true;

方式2:针对新版THREE.BufferGeometry

现在Three.js默认使用BufferGeometry,处理方式略有不同,需要直接操作UV数组:

const geometry = object.geometry;
const uvAttribute = geometry.getAttribute('uv');
const uvs = uvAttribute.array;
const groups = [];

// 遍历每个面(每个面包含3个顶点,每个顶点2个UV值)
for (let i = 0; i < uvs.length; i += 6) {
  // 获取当前面三个顶点的UV值
  const u1 = uvs[i], v1 = uvs[i+1];
  const u2 = uvs[i+2], v2 = uvs[i+3];
  const u3 = uvs[i+4], v3 = uvs[i+5];

  // 计算平均UV判断归属区间
  const avgU = (u1 + u2 + u3) / 3;
  const avgV = (v1 + v2 + v3) / 3;

  let groupIndex = 0;
  let uOffset = 0, vOffset = 0;

  if (avgU < 1 && avgV < 1) {
    groupIndex = 0;
  } else if (avgU < 1 && avgV >= 1) {
    groupIndex = 1;
    vOffset = 1;
  } else if (avgU >= 1 && avgV < 1) {
    groupIndex = 2;
    uOffset = 1;
  } else {
    groupIndex = 3;
    uOffset = 1;
    vOffset = 1;
  }

  // 调整当前面的所有UV到0-1范围
  uvs[i] -= uOffset;
  uvs[i+1] -= vOffset;
  uvs[i+2] -= uOffset;
  uvs[i+3] -= vOffset;
  uvs[i+4] -= uOffset;
  uvs[i+5] -= vOffset;

  // 添加分组:起始顶点索引、顶点数量、材质索引
  groups.push({ start: i / 2, count: 3, materialIndex: groupIndex });
}

// 更新UV属性和分组
uvAttribute.needsUpdate = true;
geometry.groups = groups;
geometry.groupsNeedUpdate = true;

第三步:创建材质数组并赋值给Mesh

最后我们创建四个材质(对应四张纹理),并把材质数组赋值给你的Mesh:

// 创建材质数组(这里用MeshBasicMaterial,你可以根据需求替换成MeshStandardMaterial等)
const materials = [
  new THREE.MeshBasicMaterial({ map: textures.u1v1, side: THREE.DoubleSide }),
  new THREE.MeshBasicMaterial({ map: textures.u1v2, side: THREE.DoubleSide }),
  new THREE.MeshBasicMaterial({ map: textures.u2v1, side: THREE.DoubleSide }),
  new THREE.MeshBasicMaterial({ map: textures.u2v2, side: THREE.DoubleSide })
];

// 给Mesh设置材质数组
object.material = materials;
// 如果你用的是非常旧的Three.js版本,可能需要用MeshFaceMaterial:
// object.material = new THREE.MeshFaceMaterial(materials);

这样处理后,四个UV区间的网格就会分别对应四张纹理,整个模型就能正确显示啦。

内容的提问来源于stack exchange,提问作者The Obscure Question

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 09:57:05