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
相关产品推荐
相关产品推荐

