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

Three.js中能否单独加载分散URL的OBJ及依赖资源?

解决方案:独立URL加载OBJ/MTL/纹理资源

当然可以!Three.js完全支持分别加载这些不在同一目录、只有独立URL的资源,不用纠结setPath用不了的问题,我给你分享两种实用的实现思路:

方法一:重写MTLLoader的纹理加载逻辑

MTLLoader默认会用MTL文件的路径拼接纹理名来加载,但我们可以直接重写它的loadTexture方法,让它使用我们从服务器拿到的独立纹理URL:

// 假设从服务器获取的资源URL列表
const resourceUrls = {
  obj: 'https://your-server.com/models/character.obj',
  mtl: 'https://your-server.com/materials/character.mtl',
  textures: {
    'skin_diffuse.jpg': 'https://your-server.com/textures/skin_01.jpg',
    'hair_normal.png': 'https://your-server.com/textures/hair_norm_02.png'
  }
};

// 初始化MTL加载器,清空默认路径避免干扰
const mtlLoader = new THREE.MTLLoader();
mtlLoader.setResourcePath('');

// 重写纹理加载逻辑
mtlLoader.loadTexture = function(url, mapping, onLoad, onProgress, onError) {
  // url是MTL文件中引用的纹理文件名,用它匹配我们的独立URL
  const actualTextureUrl = resourceUrls.textures[url];
  if (!actualTextureUrl) {
    console.warn(`找不到纹理 "${url}" 对应的URL`);
    return null;
  }
  
  const textureLoader = new THREE.TextureLoader();
  return textureLoader.load(actualTextureUrl, onLoad, onProgress, onError);
};

// 先加载MTL,再加载OBJ并应用材质
mtlLoader.load(resourceUrls.mtl, (materials) => {
  materials.preload();
  
  const objLoader = new THREE.OBJLoader();
  objLoader.setMaterials(materials);
  
  objLoader.load(
    resourceUrls.obj,
    (object) => {
      // 模型加载完成,添加到场景
      scene.add(object);
    },
    (xhr) => console.log(`OBJ加载进度: ${Math.round(xhr.loaded/xhr.total*100)}%`),
    (err) => console.error('OBJ加载失败:', err)
  );
},
(xhr) => console.log(`MTL加载进度: ${Math.round(xhr.loaded/xhr.total*100)}%`),
(err) => console.error('MTL加载失败:', err)
);

方法二:预加载所有纹理再关联

如果想更好地控制加载顺序和缓存,也可以先把所有纹理加载完成,再关联到MTL材质中:

const resourceUrls = {
  // 同上的OBJ、MTL、纹理URL
};

// 先批量加载所有纹理,返回Promise数组
const texturePromises = Object.entries(resourceUrls.textures).map(([textureName, url]) => {
  return new Promise((resolve, reject) => {
    const loader = new THREE.TextureLoader();
    loader.load(url, (texture) => {
      resolve({ name: textureName, texture });
    }, null, reject);
  });
});

// 等所有纹理加载完成后再处理MTL和OBJ
Promise.all(texturePromises).then((loadedTextures) => {
  // 把纹理存入Map,方便快速查找
  const textureMap = new Map(loadedTextures.map(t => [t.name, t.texture]));
  
  const mtlLoader = new THREE.MTLLoader();
  // 重写loadTexture,直接返回预加载好的纹理
  mtlLoader.loadTexture = function(url) {
    return textureMap.get(url) || null;
  };
  
  // 后续加载MTL和OBJ的逻辑和方法一一致
  mtlLoader.load(resourceUrls.mtl, (materials) => {
    materials.preload();
    const objLoader = new THREE.OBJLoader();
    objLoader.setMaterials(materials);
    objLoader.load(resourceUrls.obj, (object) => {
      scene.add(object);
    });
  });
}).catch((err) => {
  console.error('纹理预加载失败:', err);
});

注意事项

  • 确保MTL文件中引用的纹理文件名,和你从服务器拿到的纹理URL的键完全匹配,如果有命名差异,提前做个名称映射表即可(比如const nameMap = { 'mtl里的名字': '服务器返回的名字' })。
  • 两种方法都能绕过相对路径的限制,你可以根据项目的资源加载策略选择适合的方式。

内容的提问来源于stack exchange,提问作者pizzarob

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.25 03:48:56