[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M71CE4ZDN29MZNT5C0RXR369":3,"skill-same:01M71CE4ZDN29MZNT5C0RXR369":63},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":16,"categories":17,"keywords":18,"publishedAt":36,"linkOnly":37,"markdownContent":38,"markdownBody":39,"frontMatter":40,"refName":41,"commitSha":42,"skillDirectory":43,"forksCount":44,"repositoryHtmlUrl":45,"repositoryHomepageUrl":6,"ownerHtmlUrl":46,"ownerBlogUrl":6,"ownerDescription":47,"updatedAt":48,"install":49,"resources":62},"01M71CE4ZDN29MZNT5C0RXR369","threejs-loaders",null,"Three.js asset loading - GLTF, textures, images, models, async patterns. Use when loading 3D models, textures, HDR environments, or managing loading progress.","nexuslinkproductions\u002Fyuri-os","nexuslinkproductions","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F273246263?v=4","01M718YX9NZRCW50B59B8ZVDKN","Marcel Spatz","01M718YXAQ7530F4B1VKN6SFQV",2,"MIT","https:\u002F\u002Fgithub.com\u002Fnexuslinkproductions\u002Fyuri-os\u002Fblob\u002Fmain\u002Fskills\u002Fthreejs-loaders\u002FSKILL.md",[],[19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35],"threejs","threejs loaders","patterns","when","3d models","environments","loading","asset","progress","three","gltf","models","async","images","use","js","3D","2026-11-10 10:12:30",false,"---\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.\n---\n\n# Three.js Loaders\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.160.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### 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## See Also\n\n- `threejs-textures` - Texture configuration\n- `threejs-animation` - Playing loaded animations\n- `threejs-materials` - Material from loaded models\n","# Three.js Loaders\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.160.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### 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## See Also\n\n- `threejs-textures` - Texture configuration\n- `threejs-animation` - Playing loaded animations\n- `threejs-materials` - Material from loaded models\n",{"name":5,"description":7},"main","4c6d8d65f3d6096e2fc25b0845e481dda73c2794","skills\u002Fthreejs-loaders",0,"https:\u002F\u002Fgithub.com\u002Fnexuslinkproductions\u002Fyuri-os","https:\u002F\u002Fgithub.com\u002Fnexuslinkproductions","Building deterministic AI governance and continuity systems. Workflow architect + visual storyteller","2026-10-27 02:18:29",{"source":8,"skillName":5,"commands":50,"prompt":60,"zipAvailable":61},[51,54,57],{"tool":52,"command":53},"npx","npx skills add nexuslinkproductions\u002Fyuri-os --skill threejs-loaders",{"tool":55,"command":56},"bunx","bunx skills add nexuslinkproductions\u002Fyuri-os --skill threejs-loaders",{"tool":58,"command":59},"pnpm","pnpm dlx skills add nexuslinkproductions\u002Fyuri-os --skill threejs-loaders","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Fnexuslinkproductions\u002Fyuri-os\u002Fblob\u002Fmain\u002Fskills\u002Fthreejs-loaders\u002FSKILL.md",true,[],[64,85,109,139,168,187],{"skillNo":65,"name":66,"nameZh":6,"description":67,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":68,"categories":69,"keywords":70,"publishedAt":36},"01M71CG1AME5WC0A79AE65WT0X","zoom-out","Tell the agent to zoom out and give broader context or a higher-level perspective. Use when you're unfamiliar with a section of code or need to understand how it fits into the bigger picture.","https:\u002F\u002Fgithub.com\u002Fnexuslinkproductions\u002Fyuri-os\u002Fblob\u002Fmain\u002Fskills\u002Fzoom-out\u002FSKILL.md",[],[71,72,73,74,75,76,77,78,79,33,80,22,81,82,83,84],"zoom out","zoom","with","harness","how","section","re","true","understand","perspective","you","code","it","context",{"skillNo":86,"name":87,"nameZh":6,"description":88,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":89,"categories":90,"keywords":91,"publishedAt":36},"01M71CFY9ATD0BAXV6B14HFG7C","yuri-shura","6-perspective adversarial review for high-stakes turns (architecture decisions, refactor planning, deployment review) — fans out architect, adversary (7-vector attack), maintainer, ops, product, and security perspectives in parallel, then consolidates. Use when user says 'review this decision', '\u002Fshura', 'adversarial review', 'what could go wrong', 'architecture review', or 'deployment review'.","https:\u002F\u002Fgithub.com\u002Fnexuslinkproductions\u002Fyuri-os\u002Fblob\u002Fmain\u002Fskills\u002Fyuri-shura\u002FSKILL.md",[],[92,93,74,94,95,96,97,98,99,100,101,33,102,22,103,104,105,106,107,108],"yuri shura","parallel","architecture review","planning","for","deployment","vector","strategic review","decision","strategic","ops","workflow","decisions","architecture","review","perspectives","adversarial",{"skillNo":110,"name":111,"nameZh":6,"description":112,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":113,"categories":114,"keywords":120,"publishedAt":36},"01M71CFTTYW1HA2MNX38EV1GJ5","yuri-sales-intelligence","Unified sales and outreach skill generating cold outreach openers, social selling sequences, objection rebuttals, and ad creative for any paid platform — multi-persona aware, adapting tone and offer frame to buyer type. Use when user says 'write a cold email', 'outreach sequence', 'objection handling', 'ad copy', or 'sales pitch'.","https:\u002F\u002Fgithub.com\u002Fnexuslinkproductions\u002Fyuri-os\u002Fblob\u002Fmain\u002Fskills\u002Fyuri-sales-intelligence\u002FSKILL.md",[115],{"categoryNo":116,"categoryName":117,"taxonomyType":118,"taxonomyTypeDesc":119},"01JBSEEDCAT000000000000005","邮件与沟通","SCENARIO","使用场景",[121,122,123,124,125,126,127,128,129,22,130,131,132,133,134,135,136,96,137,138],"intelligence","sales intelligence","yuri sales intelligence","sales","cold","paid","social","persona","pitch","cold email","ad creative","write","sequences","sequence","platform","email","cold outreach","frame",{"skillNo":140,"name":141,"nameZh":6,"description":142,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":143,"categories":144,"keywords":153,"publishedAt":36},"01M71CFQMBFTFADJNGP0Z2WCYK","yuri-code-intelligence","Unified code quality intelligence layer that diagnoses code smells (Long Method, Feature Envy, design pattern issues), prescribes targeted Fowler refactoring techniques, and reviews code for security, performance, and clarity. Use when user says 'review this code', 'refactor this', 'what's wrong with this method', 'code smells', or 'clean this up'.","https:\u002F\u002Fgithub.com\u002Fnexuslinkproductions\u002Fyuri-os\u002Fblob\u002Fmain\u002Fskills\u002Fyuri-code-intelligence\u002FSKILL.md",[145,148],{"categoryNo":146,"categoryName":147,"taxonomyType":118,"taxonomyTypeDesc":119},"01JBSEEDCAT000000000000007","代码审查",{"categoryNo":149,"categoryName":150,"taxonomyType":151,"taxonomyTypeDesc":152},"01JBSEEDCAT000000000000030","开发工程师","PROFESSION","适用职业",[154,82,155,121,74,156,157,158,96,159,160,161,162,163,164,165,166,167,33,22],"yuri code intelligence","code intelligence","reviews","code quality","feature","performance","code review","long","up","issues","clean","techniques","improve","design",{"skillNo":169,"name":170,"nameZh":6,"description":171,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":172,"categories":173,"keywords":174,"publishedAt":36},"01M71CFP32RT6GD3GKNHCJP8VP","yuri-adw","Invoke when user asks to run the ADW loop \u002F agentic engineering loop \u002F software factory workflow.","https:\u002F\u002Fgithub.com\u002Fnexuslinkproductions\u002Fyuri-os\u002Fblob\u002Fmain\u002Fskills\u002Fyuri-adw\u002FSKILL.md",[],[175,176,177,178,179,180,181,182,183,22,184,103,185,186],"yuri adw","run","engineering loop","software factory","software","agentic engineering","factory","loop","invoke","engineering","agentic","user",{"skillNo":188,"name":189,"nameZh":6,"description":190,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":191,"categories":192,"keywords":205,"publishedAt":36},"01M71CFK4VPEKY6A73237P7RSN","writing-skills","Use when creating new skills, editing existing skills, or verifying skills work before deployment","https:\u002F\u002Fgithub.com\u002Fnexuslinkproductions\u002Fyuri-os\u002Fblob\u002Fmain\u002Fskills\u002Fwriting-skills\u002FSKILL.md",[193,196,199,202],{"categoryNo":194,"categoryName":195,"taxonomyType":118,"taxonomyTypeDesc":119},"01JBSEEDCAT000000000000008","测试与质量",{"categoryNo":197,"categoryName":198,"taxonomyType":118,"taxonomyTypeDesc":119},"01JBSEEDCAT000000000000018","写作与文案",{"categoryNo":200,"categoryName":201,"taxonomyType":118,"taxonomyTypeDesc":119},"01JBSEEDCAT000000000000023","Skill与智能体开发",{"categoryNo":203,"categoryName":204,"taxonomyType":151,"taxonomyTypeDesc":152},"01JBSEEDCAT000000000000036","内容创作者",[206,207,208,22,103,209,74,210,97,33,211],"writing","writing skills","new","work","editing","Existing"]