[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill-7f2c14d2-7dfe-4d5b-94c0-95ce813218c6":3,"$fgdC56AqlSV7SnyDpCBPQsgCxiwnP4mYuOnN8kImPpTM":43},{"id":4,"title":5,"description":6,"categoryId":7,"moduleId":8,"tags":9,"prompt":10,"icon":11,"source":12,"sourceUrl":13,"authorId":14,"authorName":15,"isPublic":16,"stars":17,"runs":18,"createdAt":19,"updatedAt":19,"module":20,"category":27,"packages":34},"7f2c14d2-7dfe-4d5b-94c0-95ce813218c6","threejs-loaders","Three.js 资产加载 - GLTF、纹理、图像、模型、异步模式。用于加载3D模型、纹理、HDR环境或管理加载进度。","cat_design_graphic","mod_design","sickn33,design","---\nname: threejs-loaders\ndescription: Three.js asset loading - GLTF, textures, images, models, async patterns. Use when loading 3D models, textures, HDR environments, or managing loading progress.\nrisk: unknown\nsource: community\n---\n\n# Three.js Loaders\n\n## When to Use\n- You need to load models, textures, HDR assets, or other external resources in Three.js.\n- The task involves `GLTFLoader`, `TextureLoader`, loading progress, or async asset orchestration.\n- You are managing scene assets rather than authoring geometry or shaders directly.\n\n## Quick Start\n\n```javascript\nimport * as THREE from \"three\";\nimport { GLTFLoader } from \"three\u002Faddons\u002Floaders\u002FGLTFLoader.js\";\n\n\u002F\u002F Load GLTF model\nconst loader = new GLTFLoader();\nloader.load(\"model.glb\", (gltf) => {\n  scene.add(gltf.scene);\n});\n\n\u002F\u002F Load texture\nconst textureLoader = new THREE.TextureLoader();\nconst texture = textureLoader.load(\"texture.jpg\");\n```\n\n## LoadingManager\n\nCoordinate multiple loaders and track progress.\n\n```javascript\nconst manager = new THREE.LoadingManager();\n\n\u002F\u002F Callbacks\nmanager.onStart = (url, loaded, total) => {\n  console.log(`Started loading: ${url}`);\n};\n\nmanager.onLoad = () => {\n  console.log(\"All assets loaded!\");\n  startGame();\n};\n\nmanager.onProgress = (url, loaded, total) => {\n  const progress = (loaded \u002F total) * 100;\n  console.log(`Loading: ${progress.toFixed(1)}%`);\n  updateProgressBar(progress);\n};\n\nmanager.onError = (url) => {\n  console.error(`Error loading: ${url}`);\n};\n\n\u002F\u002F Use manager with loaders\nconst textureLoader = new THREE.TextureLoader(manager);\nconst gltfLoader = new GLTFLoader(manager);\n\n\u002F\u002F Load assets\ntextureLoader.load(\"texture1.jpg\");\ntextureLoader.load(\"texture2.jpg\");\ngltfLoader.load(\"model.glb\");\n\u002F\u002F onLoad fires when ALL are complete\n```\n\n## Texture Loading\n\n### TextureLoader\n\n```javascript\nconst loader = new THREE.TextureLoader();\n\n\u002F\u002F Callback style\nloader.load(\n  \"texture.jpg\",\n  (texture) => {\n    \u002F\u002F onLoad\n    material.map = texture;\n    material.needsUpdate = true;\n  },\n  undefined, \u002F\u002F onProgress - not supported for image loading\n  (error) => {\n    \u002F\u002F onError\n    console.error(\"Error loading texture\", error);\n  },\n);\n\n\u002F\u002F Synchronous (returns texture, loads async)\nconst texture = loader.load(\"texture.jpg\");\nmaterial.map = texture;\n```\n\n### Texture Configuration\n\n```javascript\nconst texture = loader.load(\"texture.jpg\", (tex) => {\n  \u002F\u002F Color space (important for color accuracy)\n  tex.colorSpace = THREE.SRGBColorSpace; \u002F\u002F For color\u002Falbedo maps\n  \u002F\u002F tex.colorSpace = THREE.LinearSRGBColorSpace;  \u002F\u002F For data maps (normal, roughness)\n\n  \u002F\u002F Wrapping\n  tex.wrapS = THREE.RepeatWrapping;\n  tex.wrapT = THREE.RepeatWrapping;\n  \u002F\u002F ClampToEdgeWrapping, RepeatWrapping, MirroredRepeatWrapping\n\n  \u002F\u002F Repeat\u002Foffset\n  tex.repeat.set(2, 2);\n  tex.offset.set(0.5, 0.5);\n  tex.rotation = Math.PI \u002F 4;\n  tex.center.set(0.5, 0.5);\n\n  \u002F\u002F Filtering\n  tex.minFilter = THREE.LinearMipmapLinearFilter; \u002F\u002F Default\n  tex.magFilter = THREE.LinearFilter; \u002F\u002F Default\n  \u002F\u002F NearestFilter - pixelated\n  \u002F\u002F LinearFilter - smooth\n  \u002F\u002F LinearMipmapLinearFilter - smooth with mipmaps\n\n  \u002F\u002F Anisotropic filtering (sharper at angles)\n  tex.anisotropy = renderer.capabilities.getMaxAnisotropy();\n\n  \u002F\u002F Flip Y (usually true for standard textures)\n  tex.flipY = true;\n\n  tex.needsUpdate = true;\n});\n```\n\n### CubeTextureLoader\n\nFor environment maps and skyboxes.\n\n```javascript\nconst loader = new THREE.CubeTextureLoader();\n\n\u002F\u002F Load 6 faces\nconst cubeTexture = loader.load([\n  \"px.jpg\",\n  \"nx.jpg\", \u002F\u002F positive\u002Fnegative X\n  \"py.jpg\",\n  \"ny.jpg\", \u002F\u002F positive\u002Fnegative Y\n  \"pz.jpg\",\n  \"nz.jpg\", \u002F\u002F positive\u002Fnegative Z\n]);\n\n\u002F\u002F Use as background\nscene.background = cubeTexture;\n\n\u002F\u002F Use as environment map\nscene.environment = cubeTexture;\nmaterial.envMap = cubeTexture;\n```\n\n### HDR\u002FEXR Loading\n\n```javascript\nimport { RGBELoader } from \"three\u002Faddons\u002Floaders\u002FRGBELoader.js\";\nimport { EXRLoader } from \"three\u002Faddons\u002Floaders\u002FEXRLoader.js\";\n\n\u002F\u002F HDR\nconst rgbeLoader = new RGBELoader();\nrgbeLoader.load(\"environment.hdr\", (texture) => {\n  texture.mapping = THREE.EquirectangularReflectionMapping;\n  scene.environment = texture;\n  scene.background = texture;\n});\n\n\u002F\u002F EXR\nconst exrLoader = new EXRLoader();\nexrLoader.load(\"environment.exr\", (texture) => {\n  texture.mapping = THREE.EquirectangularReflectionMapping;\n  scene.environment = texture;\n});\n```\n\n### PMREMGenerator\n\nGenerate prefiltered environment maps for PBR.\n\n```javascript\nimport { RGBELoader } from \"three\u002Faddons\u002Floaders\u002FRGBELoader.js\";\n\nconst pmremGenerator = new THREE.PMREMGenerator(renderer);\npmremGenerator.compileEquirectangularShader();\n\nnew RGBELoader().load(\"environment.hdr\", (texture) => {\n  const envMap = pmremGenerator.fromEquirectangular(texture).texture;\n\n  scene.environment = envMap;\n  scene.background = envMap;\n\n  texture.dispose();\n  pmremGenerator.dispose();\n});\n```\n\n## GLTF\u002FGLB Loading\n\nThe most common 3D format for web.\n\n```javascript\nimport { GLTFLoader } from \"three\u002Faddons\u002Floaders\u002FGLTFLoader.js\";\n\nconst loader = new GLTFLoader();\n\nloader.load(\"model.glb\", (gltf) => {\n  \u002F\u002F The loaded scene\n  const model = gltf.scene;\n  scene.add(model);\n\n  \u002F\u002F Animations\n  const animations = gltf.animations;\n  if (animations.length > 0) {\n    const mixer = new THREE.AnimationMixer(model);\n    animations.forEach((clip) => {\n      mixer.clipAction(clip).play();\n    });\n  }\n\n  \u002F\u002F Cameras (if any)\n  const cameras = gltf.cameras;\n\n  \u002F\u002F Asset info\n  console.log(gltf.asset); \u002F\u002F Version, generator, etc.\n\n  \u002F\u002F User data from Blender\u002Fetc\n  console.log(gltf.userData);\n});\n```\n\n### GLTF with Draco Compression\n\n```javascript\nimport { GLTFLoader } from \"three\u002Faddons\u002Floaders\u002FGLTFLoader.js\";\nimport { DRACOLoader } from \"three\u002Faddons\u002Floaders\u002FDRACOLoader.js\";\n\nconst dracoLoader = new DRACOLoader();\ndracoLoader.setDecoderPath(\n  \"https:\u002F\u002Fwww.gstatic.com\u002Fdraco\u002Fversioned\u002Fdecoders\u002F1.5.6\u002F\",\n);\ndracoLoader.preload();\n\nconst gltfLoader = new GLTFLoader();\ngltfLoader.setDRACOLoader(dracoLoader);\n\ngltfLoader.load(\"compressed-model.glb\", (gltf) => {\n  scene.add(gltf.scene);\n});\n```\n\n### GLTF with KTX2 Textures\n\n```javascript\nimport { GLTFLoader } from \"three\u002Faddons\u002Floaders\u002FGLTFLoader.js\";\nimport { KTX2Loader } from \"three\u002Faddons\u002Floaders\u002FKTX2Loader.js\";\n\nconst ktx2Loader = new KTX2Loader();\nktx2Loader.setTranscoderPath(\n  \"https:\u002F\u002Fcdn.jsdelivr.net\u002Fnpm\u002Fthree@0.183.0\u002Fexamples\u002Fjsm\u002Flibs\u002Fbasis\u002F\",\n);\nktx2Loader.detectSupport(renderer);\n\nconst gltfLoader = new GLTFLoader();\ngltfLoader.setKTX2Loader(ktx2Loader);\n\ngltfLoader.load(\"model-with-ktx2.glb\", (gltf) => {\n  scene.add(gltf.scene);\n});\n```\n\n### GLTF with Meshopt Compression (r183)\n\n```javascript\nimport { GLTFLoader } from \"three\u002Faddons\u002Floaders\u002FGLTFLoader.js\";\nimport { MeshoptDecoder } from \"three\u002Faddons\u002Flibs\u002Fmeshopt_decoder.module.js\";\n\nconst gltfLoader = new GLTFLoader();\ngltfLoader.setMeshoptDecoder(MeshoptDecoder);\n\ngltfLoader.load(\"compressed-model.glb\", (gltf) => {\n  scene.add(gltf.scene);\n});\n```\n\n**KHR_meshopt_compression** is an alternative to Draco that often provides better compression for animated meshes and preserves mesh topology.\n\n### Process GLTF Content\n\n```javascript\nloader.load(\"model.glb\", (gltf) => {\n  const model = gltf.scene;\n\n  \u002F\u002F Enable shadows\n  model.traverse((child) => {\n    if (child.isMesh) {\n      child.castShadow = true;\n      child.receiveShadow = true;\n    }\n  });\n\n  \u002F\u002F Find specific mesh\n  const head = model.getObjectByName(\"Head\");\n\n  \u002F\u002F Adjust materials\n  model.traverse((child) => {\n    if (child.isMesh && child.material) {\n      child.material.envMapIntensity = 0.5;\n    }\n  });\n\n  \u002F\u002F Center and scale\n  const box = new THREE.Box3().setFromObject(model);\n  const center = box.getCenter(new THREE.Vector3());\n  const size = box.getSize(new THREE.Vector3());\n\n  model.position.sub(center);\n  const maxDim = Math.max(size.x, size.y, size.z);\n  model.scale.setScalar(1 \u002F maxDim);\n\n  scene.add(model);\n});\n```\n\n## Other Model Formats\n\n### OBJ + MTL\n\n```javascript\nimport { OBJLoader } from \"three\u002Faddons\u002Floaders\u002FOBJLoader.js\";\nimport { MTLLoader } from \"three\u002Faddons\u002Floaders\u002FMTLLoader.js\";\n\nconst mtlLoader = new MTLLoader();\nmtlLoader.load(\"model.mtl\", (materials) => {\n  materials.preload();\n\n  const objLoader = new OBJLoader();\n  objLoader.setMaterials(materials);\n  objLoader.load(\"model.obj\", (object) => {\n    scene.add(object);\n  });\n});\n```\n\n### FBX\n\n```javascript\nimport { FBXLoader } from \"three\u002Faddons\u002Floaders\u002FFBXLoader.js\";\n\nconst loader = new FBXLoader();\nloader.load(\"model.fbx\", (object) => {\n  \u002F\u002F FBX often has large scale\n  object.scale.setScalar(0.01);\n\n  \u002F\u002F Animations\n  const mixer = new THREE.AnimationMixer(object);\n  object.animations.forEach((clip) => {\n    mixer.clipAction(clip).play();\n  });\n\n  scene.add(object);\n});\n```\n\n### STL\n\n```javascript\nimport { STLLoader } from \"three\u002Faddons\u002Floaders\u002FSTLLoader.js\";\n\nconst loader = new STLLoader();\nloader.load(\"model.stl\", (geometry) => {\n  const material = new THREE.MeshStandardMaterial({ color: 0x888888 });\n  const mesh = new THREE.Mesh(geometry, material);\n  scene.add(mesh);\n});\n```\n\n### PLY\n\n```javascript\nimport { PLYLoader } from \"three\u002Faddons\u002Floaders\u002FPLYLoader.js\";\n\nconst loader = new PLYLoader();\nloader.load(\"model.ply\", (geometry) => {\n  geometry.computeVertexNormals();\n  const material = new THREE.MeshStandardMaterial({ vertexColors: true });\n  const mesh = new THREE.Mesh(geometry, material);\n  scene.add(mesh);\n});\n```\n\n## Async\u002FPromise Loading\n\n### Promisified Loader\n\n```javascript\nfunction loadModel(url) {\n  return new Promise((resolve, reject) => {\n    loader.load(url, resolve, undefined, reject);\n  });\n}\n\n\u002F\u002F Usage\nasync function init() {\n  try {\n    const gltf = await loadModel(\"model.glb\");\n    scene.add(gltf.scene);\n  } catch (error) {\n    console.error(\"Failed to load model:\", error);\n  }\n}\n```\n\n### Load Multiple Assets\n\n```javascript\nasync function loadAssets() {\n  const [modelGltf, envTexture, colorTexture] = await Promise.all([\n    loadGLTF(\"model.glb\"),\n    loadRGBE(\"environment.hdr\"),\n    loadTexture(\"color.jpg\"),\n  ]);\n\n  scene.add(modelGltf.scene);\n  scene.environment = envTexture;\n  material.map = colorTexture;\n}\n\n\u002F\u002F Helper functions\nfunction loadGLTF(url) {\n  return new Promise((resolve, reject) => {\n    new GLTFLoader().load(url, resolve, undefined, reject);\n  });\n}\n\nfunction loadRGBE(url) {\n  return new Promise((resolve, reject) => {\n    new RGBELoader().load(\n      url,\n      (texture) => {\n        texture.mapping = THREE.EquirectangularReflectionMapping;\n        resolve(texture);\n      },\n      undefined,\n      reject,\n    );\n  });\n}\n\nfunction loadTexture(url) {\n  return new Promise((resolve, reject) => {\n    new THREE.TextureLoader().load(url, resolve, undefined, reject);\n  });\n}\n```\n\n## Caching\n\n### Built-in Cache\n\n```javascript\n\u002F\u002F Enable cache\nTHREE.Cache.enabled = true;\n\n\u002F\u002F Clear cache\nTHREE.Cache.clear();\n\n\u002F\u002F Manual cache management\nTHREE.Cache.add(\"key\", data);\nTHREE.Cache.get(\"key\");\nTHREE.Cache.remove(\"key\");\n```\n\n### Custom Asset Manager\n\n```javascript\nclass AssetManager {\n  constructor() {\n    this.textures = new Map();\n    this.models = new Map();\n    this.gltfLoader = new GLTFLoader();\n    this.textureLoader = new THREE.TextureLoader();\n  }\n\n  async loadTexture(key, url) {\n    if (this.textures.has(key)) {\n      return this.textures.get(key);\n    }\n\n    const texture = await new Promise((resolve, reject) => {\n      this.textureLoader.load(url, resolve, undefined, reject);\n    });\n\n    this.textures.set(key, texture);\n    return texture;\n  }\n\n  async loadModel(key, url) {\n    if (this.models.has(key)) {\n      return this.models.get(key).clone();\n    }\n\n    const gltf = await new Promise((resolve, reject) => {\n      this.gltfLoader.load(url, resolve, undefined, reject);\n    });\n\n    this.models.set(key, gltf.scene);\n    return gltf.scene.clone();\n  }\n\n  dispose() {\n    this.textures.forEach((t) => t.dispose());\n    this.textures.clear();\n    this.models.clear();\n  }\n}\n\n\u002F\u002F Usage\nconst assets = new AssetManager();\nconst texture = await assets.loadTexture(\"brick\", \"brick.jpg\");\nconst model = await assets.loadModel(\"tree\", \"tree.glb\");\n```\n\n## Loading from Different Sources\n\n### Data URL \u002F Base64\n\n```javascript\nconst loader = new THREE.TextureLoader();\nconst texture = loader.load(\"data:image\u002Fpng;base64,iVBORw0KGgo...\");\n```\n\n### Blob URL\n\n```javascript\nasync function loadFromBlob(blob) {\n  const url = URL.createObjectURL(blob);\n  const texture = await loadTexture(url);\n  URL.revokeObjectURL(url);\n  return texture;\n}\n```\n\n### ArrayBuffer\n\n```javascript\n\u002F\u002F From fetch\nconst response = await fetch(\"model.glb\");\nconst buffer = await response.arrayBuffer();\n\n\u002F\u002F Parse with loader\nconst loader = new GLTFLoader();\nloader.parse(buffer, \"\", (gltf) => {\n  scene.add(gltf.scene);\n});\n```\n\n### Custom Path\u002FURL\n\n```javascript\n\u002F\u002F Set base path\nloader.setPath(\"assets\u002Fmodels\u002F\");\nloader.load(\"model.glb\"); \u002F\u002F Loads from assets\u002Fmodels\u002Fmodel.glb\n\n\u002F\u002F Set resource path (for textures referenced in model)\nloader.setResourcePath(\"assets\u002Ftextures\u002F\");\n\n\u002F\u002F Custom URL modifier\nmanager.setURLModifier((url) => {\n  return `https:\u002F\u002Fcdn.example.com\u002F${url}`;\n});\n```\n\n## Error Handling\n\n```javascript\n\u002F\u002F Graceful fallback\nasync function loadWithFallback(primaryUrl, fallbackUrl) {\n  try {\n    return await loadModel(primaryUrl);\n  } catch (error) {\n    console.warn(`Primary failed, trying fallback: ${error}`);\n    return await loadModel(fallbackUrl);\n  }\n}\n\n\u002F\u002F Retry logic\nasync function loadWithRetry(url, maxRetries = 3) {\n  for (let i = 0; i \u003C maxRetries; i++) {\n    try {\n      return await loadModel(url);\n    } catch (error) {\n      if (i === maxRetries - 1) throw error;\n      await new Promise((r) => setTimeout(r, 1000 * (i + 1)));\n    }\n  }\n}\n\n\u002F\u002F Timeout\nasync function loadWithTimeout(url, timeout = 30000) {\n  const controller = new AbortController();\n  const timeoutId = setTimeout(() => controller.abort(), timeout);\n\n  try {\n    const response = await fetch(url, { signal: controller.signal });\n    clearTimeout(timeoutId);\n    return response;\n  } catch (error) {\n    if (error.name === \"AbortError\") {\n      throw new Error(\"Loading timed out\");\n    }\n    throw error;\n  }\n}\n```\n\n## Performance Tips\n\n1. **Use compressed formats**: DRACO for geometry, KTX2\u002FBasis for textures\n2. **Load progressively**: Show placeholders while loading\n3. **Lazy load**: Only load what's needed\n4. **Use CDN**: Faster asset delivery\n5. **Enable cache**: `THREE.Cache.enabled = true`\n\n```javascript\n\u002F\u002F Progressive loading with placeholder\nconst placeholder = new THREE.Mesh(\n  new THREE.BoxGeometry(1, 1, 1),\n  new THREE.MeshBasicMaterial({ wireframe: true }),\n);\nscene.add(placeholder);\n\nloadModel(\"model.glb\").then((gltf) => {\n  scene.remove(placeholder);\n  scene.add(gltf.scene);\n});\n```\n\n## VRMLLoader Camera Support (r183)\n\nAs of r183, `VRMLLoader` supports loading cameras defined in VRML files.\n\n## See Also\n\n- `threejs-textures` - Texture configuration\n- `threejs-animation` - Playing loaded animations\n- `threejs-materials` - Material from loaded models\n\n## Limitations\n- Use this skill only when the task clearly matches the scope described above.\n- Do not treat the output as a substitute for environment-specific validation, testing, or expert review.\n- Stop and ask for clarification if required inputs, permissions, safety boundaries, or success criteria are missing.\n","","imported","https:\u002F\u002Fgithub.com\u002Fsickn33\u002Fantigravity-awesome-skills","user_system_seed","SkillOPIC",true,95,861,"2026-05-16 13:44:11",{"id":8,"name":21,"slug":22,"icon":23,"description":24,"sort":25,"createdAt":26},"设计创意","design","mdi-palette-outline","UI 设计、生成艺术、品牌视觉等创意 Skill",3,"2026-05-16 12:53:40",{"id":7,"name":28,"slug":29,"icon":30,"description":31,"moduleId":8,"sort":32,"skillCount":33,"createdAt":26},"视觉创意","graphic","mdi-brush","海报、Logo、插画等视觉创作",2,48,[35],{"id":36,"skillId":4,"version":37,"fileName":38,"fileSize":39,"filePath":40,"fileHash":41,"manifest":42,"createdAt":19},"e6bc732d-f622-4f72-9f1e-1331bd5faa53","1.0.0","threejs-loaders.zip",4735,"uploads\u002Fskills\u002F7f2c14d2-7dfe-4d5b-94c0-95ce813218c6\u002Fthreejs-loaders.zip","61325c05080cea35c8f858e8a4d8ccf6b3980be9f562ed0143e757d95af178b3","[{\"path\":\"SKILL.md\",\"isDirectory\":false,\"size\":15297}]",{"code":44,"message":45,"data":46},200,"success",{"items":47,"stats":48,"page":51},[],{"averageRating":49,"totalRatings":49,"ratingCounts":50},0,[49,49,49,49,49],{"limit":52,"offset":49,"hasMore":53,"nextOffset":52,"ratedOnly":16},15,false]