Three.js 提供了多种资源管理和加载器解决方案,以下是主要方法和工具:
Three.js 内置了多种常用格式的加载器:
javascript
// 纹理加载
const textureLoader = new THREE.TextureLoader();
textureLoader.load('texture.jpg', (texture) => {
material.map = texture;
});
// 设置回调
textureLoader.load(
'texture.jpg',
(texture) => { /* 加载成功 */ },
(progress) => { /* 加载进度 */ },
(error) => { /* 加载失败 */ }
);
javascript
// GLTF加载器(需单独引入)
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader';
const gltfLoader = new GLTFLoader();
// OBJ加载器
import { OBJLoader } from 'three/examples/jsm/loaders/OBJLoader';
// FBX加载器
import { FBXLoader } from 'three/examples/jsm/loaders/FBXLoader';
// 字体加载器
import { FontLoader } from 'three/examples/jsm/loaders/FontLoader';
javascript
const manager = new THREE.LoadingManager();
// 设置回调
manager.onStart = (url, itemsLoaded, itemsTotal) => {
console.log('开始加载:', itemsLoaded, '/', itemsTotal);
};
manager.onLoad = () => {
console.log('所有资源加载完成');
};
manager.onProgress = (url, itemsLoaded, itemsTotal) => {
console.log(`加载进度: ${itemsLoaded}/${itemsTotal}`);
updateProgressBar(itemsLoaded / itemsTotal);
};
manager.onError = (url) => {
console.error('加载失败:', url);
};
// 使用管理器
const loader = new THREE.TextureLoader(manager);
javascript
class ResourceManager {
constructor() {
this.manager = new THREE.LoadingManager();
// 存储加载完成的资源
this.resources = new Map();
this.setupManager();
}
setupManager() {
this.manager.onStart = this.onStart.bind(this);
this.manager.onProgress = this.onProgress.bind(this);
this.manager.onLoad = this.onLoad.bind(this);
this.manager.onError = this.onError.bind(this);
}
async loadResources(resourceList) {
const promises = resourceList.map(item => {
return new Promise((resolve, reject) => {
const loader = this.getLoader(item.type);
loader.load(
item.url,
(resource) => {
this.resources.set(item.name, resource);
resolve(resource);
},
undefined,
(error) => reject(error)
);
});
});
return Promise.all(promises);
}
// 多资源加载器的封装
getLoader(type) {
const loaders = {
texture: new THREE.TextureLoader(this.manager),
gltf: new GLTFLoader(this.manager),
cubeTexture: new THREE.CubeTextureLoader(this.manager)
};
return loaders[type];
}
}
javascript
function loadTexture(url) {
return new Promise((resolve, reject) => {
new THREE.TextureLoader().load(url, resolve, undefined, reject);
});
}
function loadGLTF(url) {
return new Promise((resolve, reject) => {
new GLTFLoader().load(url, resolve, undefined, reject);
});
}
// 批量加载
async function loadAllResources() {
try {
const [texture, model] = await Promise.all([
loadTexture('texture.jpg'),
loadGLTF('model.glb')
]);
return { texture, model };
} catch (error) {
console.error('资源加载失败:', error);
}
}
javascript
class AdvancedResourceManager {
constructor() {
this.cache = new Map();
this.loaders = new Map();
this.queue = [];
this.loading = false;
this.registerDefaultLoaders();
}
registerDefaultLoaders() {
this.registerLoader('texture', THREE.TextureLoader);
this.registerLoader('gltf', GLTFLoader);
this.registerLoader('audio', THREE.AudioLoader);
}
registerLoader(type, LoaderClass) {
this.loaders.set(type, LoaderClass);
}
addToQueue(type, name, url, options = {}) {
this.queue.push({ type, name, url, options });
}
async load() {
this.loading = true;
const promises = this.queue.map(item => this.loadSingle(item));
try {
const results = await Promise.all(promises);
this.loading = false;
return results;
} catch (error) {
this.loading = false;
throw error;
}
}
async loadSingle({ type, name, url, options }) {
// 检查缓存
if (this.cache.has(name)) {
return this.cache.get(name);
}
const LoaderClass = this.loaders.get(type);
if (!LoaderClass) {
throw new Error(`未注册的加载器类型: ${type}`);
}
const loader = new LoaderClass();
const resource = await new Promise((resolve, reject) => {
loader.load(url, resolve, options.onProgress, reject);
});
this.cache.set(name, resource);
return resource;
}
}
javascript
class LoadingUI {
constructor() {
this.progressBar = document.getElementById('progress-bar');
this.progressText = document.getElementById('progress-text');
this.loadingScreen = document.getElementById('loading-screen');
}
show() {
this.loadingScreen.style.display = 'flex';
}
hide() {
this.loadingScreen.style.display = 'none';
}
updateProgress(loaded, total) {
const percent = (loaded / total * 100).toFixed(1);
this.progressBar.style.width = `${percent}%`;
this.progressText.textContent = `${percent}% (${loaded}/${total})`;
}
}
// 使用示例
const loadingUI = new LoadingUI();
const manager = new THREE.LoadingManager();
manager.onProgress = (url, loaded, total) => {
loadingUI.updateProgress(loaded, total);
};
manager.onLoad = () => {
setTimeout(() => loadingUI.hide(), 500);
};
javascript
class ResourceCache {
constructor() {
this.cache = new Map();
this.maxSize = 100; // 最大缓存数
}
get(key) {
if (this.cache.has(key)) {
const item = this.cache.get(key);
// 更新使用时间
item.lastUsed = Date.now();
return item.resource;
}
return null;
}
set(key, resource) {
// 如果缓存已满,移除最久未使用的
if (this.cache.size >= this.maxSize) {
this.removeOldest();
}
this.cache.set(key, {
resource,
lastUsed: Date.now(),
size: this.calculateSize(resource)
});
}
removeOldest() {
let oldestKey = null;
let oldestTime = Infinity;
for (const [key, item] of this.cache) {
if (item.lastUsed < oldestTime) {
oldestTime = item.lastUsed;
oldestKey = key;
}
}
if (oldestKey) {
// 释放资源
this.disposeResource(this.cache.get(oldestKey).resource);
this.cache.delete(oldestKey);
}
}
disposeResource(resource) {
if (resource.dispose) resource.dispose();
if (resource.textures) {
resource.textures.forEach(texture => texture.dispose());
}
if (resource.materials) {
resource.materials.forEach(material => material.dispose());
}
if (resource.geometries) {
resource.geometries.forEach(geometry => geometry.dispose());
}
}
}
javascript
const assets = {
textures: {
wall: '/textures/wall.jpg',
floor: '/textures/floor.jpg',
skybox: '/textures/skybox/'
},
models: {
character: '/models/character.glb',
building: '/models/building.glb'
},
sounds: {
background: '/sounds/background.mp3',
effect: '/sounds/effect.mp3'
}
};
javascript
async function loadWithRetry(loaderFunc, url, maxRetries = 3, delay = 1000) {
for (let i = 0; i < maxRetries; i++) {
try {
return await loaderFunc(url);
} catch (error) {
if (i === maxRetries - 1) throw error;
console.warn(`重试加载 ${url} (${i + 1}/${maxRetries})`);
await new Promise(resolve => setTimeout(resolve, delay * (i + 1)));
}
}
}
javascript
class MemoryManager {
constructor() {
this.resources = new Set();
}
track(resource) {
this.resources.add(resource);
}
cleanup() {
for (const resource of this.resources) {
if (resource.dispose) resource.dispose();
if (resource.geometry) resource.geometry.dispose();
if (resource.material) {
if (Array.isArray(resource.material)) {
resource.material.forEach(m => m.dispose());
} else {
resource.material.dispose();
}
}
if (resource.texture) resource.texture.dispose();
}
this.resources.clear();
}
}
javascript
// 主场景资源加载器
class SceneResourceLoader {
constructor() {
this.manager = new THREE.LoadingManager();
this.loadingUI = new LoadingUI();
this.cache = new ResourceCache();
this.setupLoadingManager();
}
setupLoadingManager() {
this.manager.onStart = () => this.loadingUI.show();
this.manager.onProgress = (url, loaded, total) => {
this.loadingUI.updateProgress(loaded, total);
};
this.manager.onLoad = () => {
setTimeout(() => this.loadingUI.hide(), 300);
};
this.manager.onError = (url) => {
console.error('Failed to load:', url);
};
}
async loadSceneResources(sceneConfig) {
const promises = [];
// 加载纹理
for (const [name, url] of Object.entries(sceneConfig.textures)) {
promises.push(this.loadTexture(name, url));
}
// 加载模型
for (const [name, url] of Object.entries(sceneConfig.models)) {
promises.push(this.loadModel(name, url));
}
// 加载音频
for (const [name, url] of Object.entries(sceneConfig.audio)) {
promises.push(this.loadAudio(name, url));
}
const results = await Promise.all(promises);
return this.organizeResources(results);
}
async loadTexture(name, url) {
const cached = this.cache.get(name);
if (cached) return { type: 'texture', name, data: cached };
const texture = await new Promise((resolve, reject) => {
new THREE.TextureLoader(this.manager)
.load(url, resolve, undefined, reject);
});
texture.name = name;
this.cache.set(name, texture);
return { type: 'texture', name, data: texture };
}
// 类似的方法 loadModel, loadAudio 等
}
这些资源管理方案可以根据项目需求进行组合和扩展,以构建高效、健壮的Three.js应用资源加载系统。