[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6RAFPT5S8SPCDG6MFJW2PB5":3,"skill-same:01M6RAFPT5S8SPCDG6MFJW2PB5":64},{"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":47,"ownerDescription":48,"updatedAt":49,"install":50,"resources":63},"01M6RAFPT5S8SPCDG6MFJW2PB5","threejs-textures",null,"Three.js textures - texture types, UV mapping, environment maps, texture settings. Use when working with images, UV coordinates, cubemaps, HDR environments, or texture optimization.","Nairon-AI\u002Fagentic-starter-pack","Nairon-AI","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F260662138?v=4","01M6R8PZ1Y8CSQGFN61VACZGTH","Nairon AI","01M6R8PZ6184VR3BA88YZFBC3N",4,"MIT","https:\u002F\u002Fgithub.com\u002FNairon-AI\u002Fagentic-starter-pack\u002Fblob\u002Fmain\u002Ffrontend\u002Fthreejs\u002Fthreejs-textures\u002FSKILL.md",[],[19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35],"threejs","threejs textures","maps","when","environments","with","types","mapping","three","coordinates","Optimization","images","uv","use","environment","settings","js","2026-11-10 10:12:30",false,"---\nname: threejs-textures\ndescription: Three.js textures - texture types, UV mapping, environment maps, texture settings. Use when working with images, UV coordinates, cubemaps, HDR environments, or texture optimization.\n---\n\n# Three.js Textures\n\n## Quick Start\n\n```javascript\nimport * as THREE from \"three\";\n\n\u002F\u002F Load texture\nconst loader = new THREE.TextureLoader();\nconst texture = loader.load(\"texture.jpg\");\n\n\u002F\u002F Apply to material\nconst material = new THREE.MeshStandardMaterial({\n  map: texture,\n});\n```\n\n## Texture Loading\n\n### Basic Loading\n\n```javascript\nconst loader = new THREE.TextureLoader();\n\n\u002F\u002F Async with callbacks\nloader.load(\n  \"texture.jpg\",\n  (texture) => console.log(\"Loaded\"),\n  (progress) => console.log(\"Progress\"),\n  (error) => console.error(\"Error\"),\n);\n\n\u002F\u002F Synchronous style (loads async internally)\nconst texture = loader.load(\"texture.jpg\");\nmaterial.map = texture;\n```\n\n### Promise Wrapper\n\n```javascript\nfunction loadTexture(url) {\n  return new Promise((resolve, reject) => {\n    new THREE.TextureLoader().load(url, resolve, undefined, reject);\n  });\n}\n\n\u002F\u002F Usage\nconst [colorMap, normalMap, roughnessMap] = await Promise.all([\n  loadTexture(\"color.jpg\"),\n  loadTexture(\"normal.jpg\"),\n  loadTexture(\"roughness.jpg\"),\n]);\n```\n\n## Texture Configuration\n\n### Color Space\n\nCritical for accurate color reproduction.\n\n```javascript\n\u002F\u002F Color\u002Falbedo textures - use sRGB\ncolorTexture.colorSpace = THREE.SRGBColorSpace;\n\n\u002F\u002F Data textures (normal, roughness, metalness, AO) - leave as default\n\u002F\u002F Do NOT set colorSpace for data textures (NoColorSpace is default)\n```\n\n### Wrapping Modes\n\n```javascript\ntexture.wrapS = THREE.RepeatWrapping; \u002F\u002F Horizontal\ntexture.wrapT = THREE.RepeatWrapping; \u002F\u002F Vertical\n\n\u002F\u002F Options:\n\u002F\u002F THREE.ClampToEdgeWrapping - Stretches edge pixels (default)\n\u002F\u002F THREE.RepeatWrapping - Tiles the texture\n\u002F\u002F THREE.MirroredRepeatWrapping - Tiles with mirror flip\n```\n\n### Repeat, Offset, Rotation\n\n```javascript\n\u002F\u002F Tile texture 4x4\ntexture.repeat.set(4, 4);\ntexture.wrapS = THREE.RepeatWrapping;\ntexture.wrapT = THREE.RepeatWrapping;\n\n\u002F\u002F Offset (0-1 range)\ntexture.offset.set(0.5, 0.5);\n\n\u002F\u002F Rotation (radians, around center)\ntexture.rotation = Math.PI \u002F 4;\ntexture.center.set(0.5, 0.5); \u002F\u002F Rotation pivot\n```\n\n### Filtering\n\n```javascript\n\u002F\u002F Minification (texture larger than screen pixels)\ntexture.minFilter = THREE.LinearMipmapLinearFilter; \u002F\u002F Default, smooth\ntexture.minFilter = THREE.NearestFilter; \u002F\u002F Pixelated\ntexture.minFilter = THREE.LinearFilter; \u002F\u002F Smooth, no mipmaps\n\n\u002F\u002F Magnification (texture smaller than screen pixels)\ntexture.magFilter = THREE.LinearFilter; \u002F\u002F Smooth (default)\ntexture.magFilter = THREE.NearestFilter; \u002F\u002F Pixelated (retro games)\n\n\u002F\u002F Anisotropic filtering (sharper at angles)\ntexture.anisotropy = renderer.capabilities.getMaxAnisotropy();\n```\n\n### Generate Mipmaps\n\n```javascript\n\u002F\u002F Usually true by default\ntexture.generateMipmaps = true;\n\n\u002F\u002F Disable for non-power-of-2 textures or data textures\ntexture.generateMipmaps = false;\ntexture.minFilter = THREE.LinearFilter;\n```\n\n## Texture Types\n\n### Regular Texture\n\n```javascript\nconst texture = new THREE.Texture(image);\ntexture.needsUpdate = true;\n```\n\n### Data Texture\n\nCreate texture from raw data.\n\n```javascript\n\u002F\u002F Create gradient texture\nconst size = 256;\nconst data = new Uint8Array(size * size * 4);\n\nfor (let i = 0; i \u003C size; i++) {\n  for (let j = 0; j \u003C size; j++) {\n    const index = (i * size + j) * 4;\n    data[index] = i; \u002F\u002F R\n    data[index + 1] = j; \u002F\u002F G\n    data[index + 2] = 128; \u002F\u002F B\n    data[index + 3] = 255; \u002F\u002F A\n  }\n}\n\nconst texture = new THREE.DataTexture(data, size, size);\ntexture.needsUpdate = true;\n```\n\n### Canvas Texture\n\n```javascript\nconst canvas = document.createElement(\"canvas\");\ncanvas.width = 256;\ncanvas.height = 256;\nconst ctx = canvas.getContext(\"2d\");\n\n\u002F\u002F Draw on canvas\nctx.fillStyle = \"red\";\nctx.fillRect(0, 0, 256, 256);\nctx.fillStyle = \"white\";\nctx.font = \"48px Arial\";\nctx.fillText(\"Hello\", 50, 150);\n\nconst texture = new THREE.CanvasTexture(canvas);\n\n\u002F\u002F Update when canvas changes\ntexture.needsUpdate = true;\n```\n\n### Video Texture\n\n```javascript\nconst video = document.createElement(\"video\");\nvideo.src = \"video.mp4\";\nvideo.loop = true;\nvideo.muted = true;\nvideo.play();\n\nconst texture = new THREE.VideoTexture(video);\ntexture.colorSpace = THREE.SRGBColorSpace;\n\n\u002F\u002F No need to set needsUpdate - auto-updates\n```\n\n### Compressed Textures\n\n```javascript\nimport { KTX2Loader } from \"three\u002Fexamples\u002Fjsm\u002Floaders\u002FKTX2Loader.js\";\n\nconst ktx2Loader = new KTX2Loader();\nktx2Loader.setTranscoderPath(\"path\u002Fto\u002Fbasis\u002F\");\nktx2Loader.detectSupport(renderer);\n\nktx2Loader.load(\"texture.ktx2\", (texture) => {\n  material.map = texture;\n});\n```\n\n## Cube Textures\n\nFor environment maps and skyboxes.\n\n### CubeTextureLoader\n\n```javascript\nconst loader = new THREE.CubeTextureLoader();\nconst cubeTexture = loader.load([\n  \"px.jpg\",\n  \"nx.jpg\", \u002F\u002F +X, -X\n  \"py.jpg\",\n  \"ny.jpg\", \u002F\u002F +Y, -Y\n  \"pz.jpg\",\n  \"nz.jpg\", \u002F\u002F +Z, -Z\n]);\n\n\u002F\u002F As background\nscene.background = cubeTexture;\n\n\u002F\u002F As environment map\nscene.environment = cubeTexture;\nmaterial.envMap = cubeTexture;\n```\n\n### Equirectangular to Cubemap\n\n```javascript\nimport { RGBELoader } from \"three\u002Fexamples\u002Fjsm\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  scene.environment = envMap;\n  scene.background = envMap;\n\n  texture.dispose();\n  pmremGenerator.dispose();\n});\n```\n\n## HDR Textures\n\n### RGBELoader\n\n```javascript\nimport { RGBELoader } from \"three\u002Fexamples\u002Fjsm\u002Floaders\u002FRGBELoader.js\";\n\nconst loader = new RGBELoader();\nloader.load(\"environment.hdr\", (texture) => {\n  texture.mapping = THREE.EquirectangularReflectionMapping;\n  scene.environment = texture;\n  scene.background = texture;\n});\n```\n\n### EXRLoader\n\n```javascript\nimport { EXRLoader } from \"three\u002Fexamples\u002Fjsm\u002Floaders\u002FEXRLoader.js\";\n\nconst loader = new EXRLoader();\nloader.load(\"environment.exr\", (texture) => {\n  texture.mapping = THREE.EquirectangularReflectionMapping;\n  scene.environment = texture;\n});\n```\n\n### Background Options\n\n```javascript\nscene.background = texture;\nscene.backgroundBlurriness = 0.5; \u002F\u002F 0-1, blur background\nscene.backgroundIntensity = 1.0; \u002F\u002F Brightness\nscene.backgroundRotation.y = Math.PI; \u002F\u002F Rotate background\n```\n\n## Render Targets\n\nRender to texture for effects.\n\n```javascript\n\u002F\u002F Create render target\nconst renderTarget = new THREE.WebGLRenderTarget(512, 512, {\n  minFilter: THREE.LinearFilter,\n  magFilter: THREE.LinearFilter,\n  format: THREE.RGBAFormat,\n});\n\n\u002F\u002F Render scene to target\nrenderer.setRenderTarget(renderTarget);\nrenderer.render(scene, camera);\nrenderer.setRenderTarget(null); \u002F\u002F Back to screen\n\n\u002F\u002F Use as texture\nmaterial.map = renderTarget.texture;\n```\n\n### Depth Texture\n\n```javascript\nconst renderTarget = new THREE.WebGLRenderTarget(512, 512);\nrenderTarget.depthTexture = new THREE.DepthTexture(\n  512,\n  512,\n  THREE.UnsignedShortType,\n);\n\n\u002F\u002F Access depth\nconst depthTexture = renderTarget.depthTexture;\n```\n\n### Multi-Sample Render Target\n\n```javascript\nconst renderTarget = new THREE.WebGLRenderTarget(512, 512, {\n  samples: 4, \u002F\u002F MSAA\n});\n```\n\n## CubeCamera\n\nDynamic environment maps for reflections.\n\n```javascript\nconst cubeRenderTarget = new THREE.WebGLCubeRenderTarget(256, {\n  generateMipmaps: true,\n  minFilter: THREE.LinearMipmapLinearFilter,\n});\n\nconst cubeCamera = new THREE.CubeCamera(0.1, 1000, cubeRenderTarget);\nscene.add(cubeCamera);\n\n\u002F\u002F Apply to reflective material\nreflectiveMaterial.envMap = cubeRenderTarget.texture;\n\n\u002F\u002F Update in animation loop (expensive!)\nfunction animate() {\n  \u002F\u002F Hide reflective object, update env map, show again\n  reflectiveObject.visible = false;\n  cubeCamera.position.copy(reflectiveObject.position);\n  cubeCamera.update(renderer, scene);\n  reflectiveObject.visible = true;\n}\n```\n\n## UV Mapping\n\n### Accessing UVs\n\n```javascript\nconst uvs = geometry.attributes.uv;\n\n\u002F\u002F Read UV\nconst u = uvs.getX(vertexIndex);\nconst v = uvs.getY(vertexIndex);\n\n\u002F\u002F Modify UV\nuvs.setXY(vertexIndex, newU, newV);\nuvs.needsUpdate = true;\n```\n\n### Second UV Channel (for AO maps)\n\n```javascript\n\u002F\u002F Required for aoMap\ngeometry.setAttribute(\"uv2\", geometry.attributes.uv);\n\n\u002F\u002F Or create custom second UV\nconst uv2 = new Float32Array(vertexCount * 2);\n\u002F\u002F ... fill uv2 data\ngeometry.setAttribute(\"uv2\", new THREE.BufferAttribute(uv2, 2));\n```\n\n### UV Transform in Shader\n\n```javascript\nconst material = new THREE.ShaderMaterial({\n  uniforms: {\n    map: { value: texture },\n    uvOffset: { value: new THREE.Vector2(0, 0) },\n    uvScale: { value: new THREE.Vector2(1, 1) },\n  },\n  vertexShader: `\n    varying vec2 vUv;\n    uniform vec2 uvOffset;\n    uniform vec2 uvScale;\n\n    void main() {\n      vUv = uv * uvScale + uvOffset;\n      gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);\n    }\n  `,\n  fragmentShader: `\n    varying vec2 vUv;\n    uniform sampler2D map;\n\n    void main() {\n      gl_FragColor = texture2D(map, vUv);\n    }\n  `,\n});\n```\n\n## Texture Atlas\n\nMultiple images in one texture.\n\n```javascript\n\u002F\u002F Atlas with 4 sprites (2x2 grid)\nconst atlas = loader.load(\"atlas.png\");\natlas.wrapS = THREE.ClampToEdgeWrapping;\natlas.wrapT = THREE.ClampToEdgeWrapping;\n\n\u002F\u002F Select sprite by UV offset\u002Fscale\nfunction selectSprite(row, col, gridSize = 2) {\n  atlas.offset.set(col \u002F gridSize, 1 - (row + 1) \u002F gridSize);\n  atlas.repeat.set(1 \u002F gridSize, 1 \u002F gridSize);\n}\n\n\u002F\u002F Select top-left sprite\nselectSprite(0, 0);\n```\n\n## Material Texture Maps\n\n### PBR Texture Set\n\n```javascript\nconst material = new THREE.MeshStandardMaterial({\n  \u002F\u002F Base color (sRGB)\n  map: colorTexture,\n\n  \u002F\u002F Surface detail (Linear)\n  normalMap: normalTexture,\n  normalScale: new THREE.Vector2(1, 1),\n\n  \u002F\u002F Roughness (Linear, grayscale)\n  roughnessMap: roughnessTexture,\n  roughness: 1, \u002F\u002F Multiplier\n\n  \u002F\u002F Metalness (Linear, grayscale)\n  metalnessMap: metalnessTexture,\n  metalness: 1, \u002F\u002F Multiplier\n\n  \u002F\u002F Ambient occlusion (Linear, uses uv2)\n  aoMap: aoTexture,\n  aoMapIntensity: 1,\n\n  \u002F\u002F Self-illumination (sRGB)\n  emissiveMap: emissiveTexture,\n  emissive: 0xffffff,\n  emissiveIntensity: 1,\n\n  \u002F\u002F Vertex displacement (Linear)\n  displacementMap: displacementTexture,\n  displacementScale: 0.1,\n  displacementBias: 0,\n\n  \u002F\u002F Alpha (Linear)\n  alphaMap: alphaTexture,\n  transparent: true,\n});\n\n\u002F\u002F Don't forget UV2 for AO\ngeometry.setAttribute(\"uv2\", geometry.attributes.uv);\n```\n\n### Normal Map Types\n\n```javascript\n\u002F\u002F OpenGL style normals (default)\nmaterial.normalMapType = THREE.TangentSpaceNormalMap;\n\n\u002F\u002F Object space normals\nmaterial.normalMapType = THREE.ObjectSpaceNormalMap;\n```\n\n## Procedural Textures\n\n### Noise Texture\n\n```javascript\nfunction generateNoiseTexture(size = 256) {\n  const data = new Uint8Array(size * size * 4);\n\n  for (let i = 0; i \u003C size * size; i++) {\n    const value = Math.random() * 255;\n    data[i * 4] = value;\n    data[i * 4 + 1] = value;\n    data[i * 4 + 2] = value;\n    data[i * 4 + 3] = 255;\n  }\n\n  const texture = new THREE.DataTexture(data, size, size);\n  texture.needsUpdate = true;\n  return texture;\n}\n```\n\n### Gradient Texture\n\n```javascript\nfunction generateGradientTexture(color1, color2, size = 256) {\n  const canvas = document.createElement(\"canvas\");\n  canvas.width = size;\n  canvas.height = 1;\n  const ctx = canvas.getContext(\"2d\");\n\n  const gradient = ctx.createLinearGradient(0, 0, size, 0);\n  gradient.addColorStop(0, color1);\n  gradient.addColorStop(1, color2);\n\n  ctx.fillStyle = gradient;\n  ctx.fillRect(0, 0, size, 1);\n\n  return new THREE.CanvasTexture(canvas);\n}\n```\n\n## Texture Memory Management\n\n### Dispose Textures\n\n```javascript\n\u002F\u002F Single texture\ntexture.dispose();\n\n\u002F\u002F Material textures\nfunction disposeMaterial(material) {\n  const maps = [\n    \"map\",\n    \"normalMap\",\n    \"roughnessMap\",\n    \"metalnessMap\",\n    \"aoMap\",\n    \"emissiveMap\",\n    \"displacementMap\",\n    \"alphaMap\",\n    \"envMap\",\n    \"lightMap\",\n    \"bumpMap\",\n    \"specularMap\",\n  ];\n\n  maps.forEach((mapName) => {\n    if (material[mapName]) {\n      material[mapName].dispose();\n    }\n  });\n\n  material.dispose();\n}\n```\n\n### Texture Pooling\n\n```javascript\nclass TexturePool {\n  constructor() {\n    this.textures = new Map();\n    this.loader = new THREE.TextureLoader();\n  }\n\n  async get(url) {\n    if (this.textures.has(url)) {\n      return this.textures.get(url);\n    }\n\n    const texture = await new Promise((resolve, reject) => {\n      this.loader.load(url, resolve, undefined, reject);\n    });\n\n    this.textures.set(url, texture);\n    return texture;\n  }\n\n  dispose(url) {\n    const texture = this.textures.get(url);\n    if (texture) {\n      texture.dispose();\n      this.textures.delete(url);\n    }\n  }\n\n  disposeAll() {\n    this.textures.forEach((t) => t.dispose());\n    this.textures.clear();\n  }\n}\n```\n\n## Performance Tips\n\n1. **Use power-of-2 dimensions**: 256, 512, 1024, 2048\n2. **Compress textures**: KTX2\u002FBasis for web delivery\n3. **Use texture atlases**: Reduce texture switches\n4. **Enable mipmaps**: For distant objects\n5. **Limit texture size**: 2048 usually sufficient for web\n6. **Reuse textures**: Same texture = better batching\n\n```javascript\n\u002F\u002F Check texture memory\nconsole.log(renderer.info.memory.textures);\n\n\u002F\u002F Optimize for mobile\nconst maxSize = renderer.capabilities.maxTextureSize;\nconst isMobile = \u002FiPhone|iPad|Android\u002Fi.test(navigator.userAgent);\nconst textureSize = isMobile ? 1024 : 2048;\n```\n\n## See Also\n\n- `threejs-materials` - Applying textures to materials\n- `threejs-loaders` - Loading texture files\n- `threejs-shaders` - Custom texture sampling\n","# Three.js Textures\n\n## Quick Start\n\n```javascript\nimport * as THREE from \"three\";\n\n\u002F\u002F Load texture\nconst loader = new THREE.TextureLoader();\nconst texture = loader.load(\"texture.jpg\");\n\n\u002F\u002F Apply to material\nconst material = new THREE.MeshStandardMaterial({\n  map: texture,\n});\n```\n\n## Texture Loading\n\n### Basic Loading\n\n```javascript\nconst loader = new THREE.TextureLoader();\n\n\u002F\u002F Async with callbacks\nloader.load(\n  \"texture.jpg\",\n  (texture) => console.log(\"Loaded\"),\n  (progress) => console.log(\"Progress\"),\n  (error) => console.error(\"Error\"),\n);\n\n\u002F\u002F Synchronous style (loads async internally)\nconst texture = loader.load(\"texture.jpg\");\nmaterial.map = texture;\n```\n\n### Promise Wrapper\n\n```javascript\nfunction loadTexture(url) {\n  return new Promise((resolve, reject) => {\n    new THREE.TextureLoader().load(url, resolve, undefined, reject);\n  });\n}\n\n\u002F\u002F Usage\nconst [colorMap, normalMap, roughnessMap] = await Promise.all([\n  loadTexture(\"color.jpg\"),\n  loadTexture(\"normal.jpg\"),\n  loadTexture(\"roughness.jpg\"),\n]);\n```\n\n## Texture Configuration\n\n### Color Space\n\nCritical for accurate color reproduction.\n\n```javascript\n\u002F\u002F Color\u002Falbedo textures - use sRGB\ncolorTexture.colorSpace = THREE.SRGBColorSpace;\n\n\u002F\u002F Data textures (normal, roughness, metalness, AO) - leave as default\n\u002F\u002F Do NOT set colorSpace for data textures (NoColorSpace is default)\n```\n\n### Wrapping Modes\n\n```javascript\ntexture.wrapS = THREE.RepeatWrapping; \u002F\u002F Horizontal\ntexture.wrapT = THREE.RepeatWrapping; \u002F\u002F Vertical\n\n\u002F\u002F Options:\n\u002F\u002F THREE.ClampToEdgeWrapping - Stretches edge pixels (default)\n\u002F\u002F THREE.RepeatWrapping - Tiles the texture\n\u002F\u002F THREE.MirroredRepeatWrapping - Tiles with mirror flip\n```\n\n### Repeat, Offset, Rotation\n\n```javascript\n\u002F\u002F Tile texture 4x4\ntexture.repeat.set(4, 4);\ntexture.wrapS = THREE.RepeatWrapping;\ntexture.wrapT = THREE.RepeatWrapping;\n\n\u002F\u002F Offset (0-1 range)\ntexture.offset.set(0.5, 0.5);\n\n\u002F\u002F Rotation (radians, around center)\ntexture.rotation = Math.PI \u002F 4;\ntexture.center.set(0.5, 0.5); \u002F\u002F Rotation pivot\n```\n\n### Filtering\n\n```javascript\n\u002F\u002F Minification (texture larger than screen pixels)\ntexture.minFilter = THREE.LinearMipmapLinearFilter; \u002F\u002F Default, smooth\ntexture.minFilter = THREE.NearestFilter; \u002F\u002F Pixelated\ntexture.minFilter = THREE.LinearFilter; \u002F\u002F Smooth, no mipmaps\n\n\u002F\u002F Magnification (texture smaller than screen pixels)\ntexture.magFilter = THREE.LinearFilter; \u002F\u002F Smooth (default)\ntexture.magFilter = THREE.NearestFilter; \u002F\u002F Pixelated (retro games)\n\n\u002F\u002F Anisotropic filtering (sharper at angles)\ntexture.anisotropy = renderer.capabilities.getMaxAnisotropy();\n```\n\n### Generate Mipmaps\n\n```javascript\n\u002F\u002F Usually true by default\ntexture.generateMipmaps = true;\n\n\u002F\u002F Disable for non-power-of-2 textures or data textures\ntexture.generateMipmaps = false;\ntexture.minFilter = THREE.LinearFilter;\n```\n\n## Texture Types\n\n### Regular Texture\n\n```javascript\nconst texture = new THREE.Texture(image);\ntexture.needsUpdate = true;\n```\n\n### Data Texture\n\nCreate texture from raw data.\n\n```javascript\n\u002F\u002F Create gradient texture\nconst size = 256;\nconst data = new Uint8Array(size * size * 4);\n\nfor (let i = 0; i \u003C size; i++) {\n  for (let j = 0; j \u003C size; j++) {\n    const index = (i * size + j) * 4;\n    data[index] = i; \u002F\u002F R\n    data[index + 1] = j; \u002F\u002F G\n    data[index + 2] = 128; \u002F\u002F B\n    data[index + 3] = 255; \u002F\u002F A\n  }\n}\n\nconst texture = new THREE.DataTexture(data, size, size);\ntexture.needsUpdate = true;\n```\n\n### Canvas Texture\n\n```javascript\nconst canvas = document.createElement(\"canvas\");\ncanvas.width = 256;\ncanvas.height = 256;\nconst ctx = canvas.getContext(\"2d\");\n\n\u002F\u002F Draw on canvas\nctx.fillStyle = \"red\";\nctx.fillRect(0, 0, 256, 256);\nctx.fillStyle = \"white\";\nctx.font = \"48px Arial\";\nctx.fillText(\"Hello\", 50, 150);\n\nconst texture = new THREE.CanvasTexture(canvas);\n\n\u002F\u002F Update when canvas changes\ntexture.needsUpdate = true;\n```\n\n### Video Texture\n\n```javascript\nconst video = document.createElement(\"video\");\nvideo.src = \"video.mp4\";\nvideo.loop = true;\nvideo.muted = true;\nvideo.play();\n\nconst texture = new THREE.VideoTexture(video);\ntexture.colorSpace = THREE.SRGBColorSpace;\n\n\u002F\u002F No need to set needsUpdate - auto-updates\n```\n\n### Compressed Textures\n\n```javascript\nimport { KTX2Loader } from \"three\u002Fexamples\u002Fjsm\u002Floaders\u002FKTX2Loader.js\";\n\nconst ktx2Loader = new KTX2Loader();\nktx2Loader.setTranscoderPath(\"path\u002Fto\u002Fbasis\u002F\");\nktx2Loader.detectSupport(renderer);\n\nktx2Loader.load(\"texture.ktx2\", (texture) => {\n  material.map = texture;\n});\n```\n\n## Cube Textures\n\nFor environment maps and skyboxes.\n\n### CubeTextureLoader\n\n```javascript\nconst loader = new THREE.CubeTextureLoader();\nconst cubeTexture = loader.load([\n  \"px.jpg\",\n  \"nx.jpg\", \u002F\u002F +X, -X\n  \"py.jpg\",\n  \"ny.jpg\", \u002F\u002F +Y, -Y\n  \"pz.jpg\",\n  \"nz.jpg\", \u002F\u002F +Z, -Z\n]);\n\n\u002F\u002F As background\nscene.background = cubeTexture;\n\n\u002F\u002F As environment map\nscene.environment = cubeTexture;\nmaterial.envMap = cubeTexture;\n```\n\n### Equirectangular to Cubemap\n\n```javascript\nimport { RGBELoader } from \"three\u002Fexamples\u002Fjsm\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  scene.environment = envMap;\n  scene.background = envMap;\n\n  texture.dispose();\n  pmremGenerator.dispose();\n});\n```\n\n## HDR Textures\n\n### RGBELoader\n\n```javascript\nimport { RGBELoader } from \"three\u002Fexamples\u002Fjsm\u002Floaders\u002FRGBELoader.js\";\n\nconst loader = new RGBELoader();\nloader.load(\"environment.hdr\", (texture) => {\n  texture.mapping = THREE.EquirectangularReflectionMapping;\n  scene.environment = texture;\n  scene.background = texture;\n});\n```\n\n### EXRLoader\n\n```javascript\nimport { EXRLoader } from \"three\u002Fexamples\u002Fjsm\u002Floaders\u002FEXRLoader.js\";\n\nconst loader = new EXRLoader();\nloader.load(\"environment.exr\", (texture) => {\n  texture.mapping = THREE.EquirectangularReflectionMapping;\n  scene.environment = texture;\n});\n```\n\n### Background Options\n\n```javascript\nscene.background = texture;\nscene.backgroundBlurriness = 0.5; \u002F\u002F 0-1, blur background\nscene.backgroundIntensity = 1.0; \u002F\u002F Brightness\nscene.backgroundRotation.y = Math.PI; \u002F\u002F Rotate background\n```\n\n## Render Targets\n\nRender to texture for effects.\n\n```javascript\n\u002F\u002F Create render target\nconst renderTarget = new THREE.WebGLRenderTarget(512, 512, {\n  minFilter: THREE.LinearFilter,\n  magFilter: THREE.LinearFilter,\n  format: THREE.RGBAFormat,\n});\n\n\u002F\u002F Render scene to target\nrenderer.setRenderTarget(renderTarget);\nrenderer.render(scene, camera);\nrenderer.setRenderTarget(null); \u002F\u002F Back to screen\n\n\u002F\u002F Use as texture\nmaterial.map = renderTarget.texture;\n```\n\n### Depth Texture\n\n```javascript\nconst renderTarget = new THREE.WebGLRenderTarget(512, 512);\nrenderTarget.depthTexture = new THREE.DepthTexture(\n  512,\n  512,\n  THREE.UnsignedShortType,\n);\n\n\u002F\u002F Access depth\nconst depthTexture = renderTarget.depthTexture;\n```\n\n### Multi-Sample Render Target\n\n```javascript\nconst renderTarget = new THREE.WebGLRenderTarget(512, 512, {\n  samples: 4, \u002F\u002F MSAA\n});\n```\n\n## CubeCamera\n\nDynamic environment maps for reflections.\n\n```javascript\nconst cubeRenderTarget = new THREE.WebGLCubeRenderTarget(256, {\n  generateMipmaps: true,\n  minFilter: THREE.LinearMipmapLinearFilter,\n});\n\nconst cubeCamera = new THREE.CubeCamera(0.1, 1000, cubeRenderTarget);\nscene.add(cubeCamera);\n\n\u002F\u002F Apply to reflective material\nreflectiveMaterial.envMap = cubeRenderTarget.texture;\n\n\u002F\u002F Update in animation loop (expensive!)\nfunction animate() {\n  \u002F\u002F Hide reflective object, update env map, show again\n  reflectiveObject.visible = false;\n  cubeCamera.position.copy(reflectiveObject.position);\n  cubeCamera.update(renderer, scene);\n  reflectiveObject.visible = true;\n}\n```\n\n## UV Mapping\n\n### Accessing UVs\n\n```javascript\nconst uvs = geometry.attributes.uv;\n\n\u002F\u002F Read UV\nconst u = uvs.getX(vertexIndex);\nconst v = uvs.getY(vertexIndex);\n\n\u002F\u002F Modify UV\nuvs.setXY(vertexIndex, newU, newV);\nuvs.needsUpdate = true;\n```\n\n### Second UV Channel (for AO maps)\n\n```javascript\n\u002F\u002F Required for aoMap\ngeometry.setAttribute(\"uv2\", geometry.attributes.uv);\n\n\u002F\u002F Or create custom second UV\nconst uv2 = new Float32Array(vertexCount * 2);\n\u002F\u002F ... fill uv2 data\ngeometry.setAttribute(\"uv2\", new THREE.BufferAttribute(uv2, 2));\n```\n\n### UV Transform in Shader\n\n```javascript\nconst material = new THREE.ShaderMaterial({\n  uniforms: {\n    map: { value: texture },\n    uvOffset: { value: new THREE.Vector2(0, 0) },\n    uvScale: { value: new THREE.Vector2(1, 1) },\n  },\n  vertexShader: `\n    varying vec2 vUv;\n    uniform vec2 uvOffset;\n    uniform vec2 uvScale;\n\n    void main() {\n      vUv = uv * uvScale + uvOffset;\n      gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);\n    }\n  `,\n  fragmentShader: `\n    varying vec2 vUv;\n    uniform sampler2D map;\n\n    void main() {\n      gl_FragColor = texture2D(map, vUv);\n    }\n  `,\n});\n```\n\n## Texture Atlas\n\nMultiple images in one texture.\n\n```javascript\n\u002F\u002F Atlas with 4 sprites (2x2 grid)\nconst atlas = loader.load(\"atlas.png\");\natlas.wrapS = THREE.ClampToEdgeWrapping;\natlas.wrapT = THREE.ClampToEdgeWrapping;\n\n\u002F\u002F Select sprite by UV offset\u002Fscale\nfunction selectSprite(row, col, gridSize = 2) {\n  atlas.offset.set(col \u002F gridSize, 1 - (row + 1) \u002F gridSize);\n  atlas.repeat.set(1 \u002F gridSize, 1 \u002F gridSize);\n}\n\n\u002F\u002F Select top-left sprite\nselectSprite(0, 0);\n```\n\n## Material Texture Maps\n\n### PBR Texture Set\n\n```javascript\nconst material = new THREE.MeshStandardMaterial({\n  \u002F\u002F Base color (sRGB)\n  map: colorTexture,\n\n  \u002F\u002F Surface detail (Linear)\n  normalMap: normalTexture,\n  normalScale: new THREE.Vector2(1, 1),\n\n  \u002F\u002F Roughness (Linear, grayscale)\n  roughnessMap: roughnessTexture,\n  roughness: 1, \u002F\u002F Multiplier\n\n  \u002F\u002F Metalness (Linear, grayscale)\n  metalnessMap: metalnessTexture,\n  metalness: 1, \u002F\u002F Multiplier\n\n  \u002F\u002F Ambient occlusion (Linear, uses uv2)\n  aoMap: aoTexture,\n  aoMapIntensity: 1,\n\n  \u002F\u002F Self-illumination (sRGB)\n  emissiveMap: emissiveTexture,\n  emissive: 0xffffff,\n  emissiveIntensity: 1,\n\n  \u002F\u002F Vertex displacement (Linear)\n  displacementMap: displacementTexture,\n  displacementScale: 0.1,\n  displacementBias: 0,\n\n  \u002F\u002F Alpha (Linear)\n  alphaMap: alphaTexture,\n  transparent: true,\n});\n\n\u002F\u002F Don't forget UV2 for AO\ngeometry.setAttribute(\"uv2\", geometry.attributes.uv);\n```\n\n### Normal Map Types\n\n```javascript\n\u002F\u002F OpenGL style normals (default)\nmaterial.normalMapType = THREE.TangentSpaceNormalMap;\n\n\u002F\u002F Object space normals\nmaterial.normalMapType = THREE.ObjectSpaceNormalMap;\n```\n\n## Procedural Textures\n\n### Noise Texture\n\n```javascript\nfunction generateNoiseTexture(size = 256) {\n  const data = new Uint8Array(size * size * 4);\n\n  for (let i = 0; i \u003C size * size; i++) {\n    const value = Math.random() * 255;\n    data[i * 4] = value;\n    data[i * 4 + 1] = value;\n    data[i * 4 + 2] = value;\n    data[i * 4 + 3] = 255;\n  }\n\n  const texture = new THREE.DataTexture(data, size, size);\n  texture.needsUpdate = true;\n  return texture;\n}\n```\n\n### Gradient Texture\n\n```javascript\nfunction generateGradientTexture(color1, color2, size = 256) {\n  const canvas = document.createElement(\"canvas\");\n  canvas.width = size;\n  canvas.height = 1;\n  const ctx = canvas.getContext(\"2d\");\n\n  const gradient = ctx.createLinearGradient(0, 0, size, 0);\n  gradient.addColorStop(0, color1);\n  gradient.addColorStop(1, color2);\n\n  ctx.fillStyle = gradient;\n  ctx.fillRect(0, 0, size, 1);\n\n  return new THREE.CanvasTexture(canvas);\n}\n```\n\n## Texture Memory Management\n\n### Dispose Textures\n\n```javascript\n\u002F\u002F Single texture\ntexture.dispose();\n\n\u002F\u002F Material textures\nfunction disposeMaterial(material) {\n  const maps = [\n    \"map\",\n    \"normalMap\",\n    \"roughnessMap\",\n    \"metalnessMap\",\n    \"aoMap\",\n    \"emissiveMap\",\n    \"displacementMap\",\n    \"alphaMap\",\n    \"envMap\",\n    \"lightMap\",\n    \"bumpMap\",\n    \"specularMap\",\n  ];\n\n  maps.forEach((mapName) => {\n    if (material[mapName]) {\n      material[mapName].dispose();\n    }\n  });\n\n  material.dispose();\n}\n```\n\n### Texture Pooling\n\n```javascript\nclass TexturePool {\n  constructor() {\n    this.textures = new Map();\n    this.loader = new THREE.TextureLoader();\n  }\n\n  async get(url) {\n    if (this.textures.has(url)) {\n      return this.textures.get(url);\n    }\n\n    const texture = await new Promise((resolve, reject) => {\n      this.loader.load(url, resolve, undefined, reject);\n    });\n\n    this.textures.set(url, texture);\n    return texture;\n  }\n\n  dispose(url) {\n    const texture = this.textures.get(url);\n    if (texture) {\n      texture.dispose();\n      this.textures.delete(url);\n    }\n  }\n\n  disposeAll() {\n    this.textures.forEach((t) => t.dispose());\n    this.textures.clear();\n  }\n}\n```\n\n## Performance Tips\n\n1. **Use power-of-2 dimensions**: 256, 512, 1024, 2048\n2. **Compress textures**: KTX2\u002FBasis for web delivery\n3. **Use texture atlases**: Reduce texture switches\n4. **Enable mipmaps**: For distant objects\n5. **Limit texture size**: 2048 usually sufficient for web\n6. **Reuse textures**: Same texture = better batching\n\n```javascript\n\u002F\u002F Check texture memory\nconsole.log(renderer.info.memory.textures);\n\n\u002F\u002F Optimize for mobile\nconst maxSize = renderer.capabilities.maxTextureSize;\nconst isMobile = \u002FiPhone|iPad|Android\u002Fi.test(navigator.userAgent);\nconst textureSize = isMobile ? 1024 : 2048;\n```\n\n## See Also\n\n- `threejs-materials` - Applying textures to materials\n- `threejs-loaders` - Loading texture files\n- `threejs-shaders` - Custom texture sampling\n",{"name":5,"description":7},"main","c9d0c408715983e993b2f9ebffdbfa7f10153bcb","frontend\u002Fthreejs\u002Fthreejs-textures",0,"https:\u002F\u002Fgithub.com\u002FNairon-AI\u002Fagentic-starter-pack","https:\u002F\u002Fgithub.com\u002FNairon-AI","https:\u002F\u002Fwww.naironai.com\u002F","Building the future of Work","2026-09-20 17:40:05",{"source":8,"skillName":5,"commands":51,"prompt":61,"zipAvailable":62},[52,55,58],{"tool":53,"command":54},"npx","npx skills add Nairon-AI\u002Fagentic-starter-pack --skill threejs-textures",{"tool":56,"command":57},"bunx","bunx skills add Nairon-AI\u002Fagentic-starter-pack --skill threejs-textures",{"tool":59,"command":60},"pnpm","pnpm dlx skills add Nairon-AI\u002Fagentic-starter-pack --skill threejs-textures","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002FNairon-AI\u002Fagentic-starter-pack\u002Fblob\u002Fmain\u002Ffrontend\u002Fthreejs\u002Fthreejs-textures\u002FSKILL.md",true,[],[65,100,122,143,163,184],{"skillNo":66,"name":67,"nameZh":6,"description":68,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":69,"categories":70,"keywords":81,"publishedAt":36},"01M6RBCG630Y8X99XWR9Q4GYNY","writing-shape","Take a markdown file of raw material and shape it into an article through a conversational session — drafting candidate openings, growing the piece paragraph by paragraph, arguing about format (lists, tables, callouts, quotes) at each step. Use when the user has a pile of notes, fragments, or a rough draft and wants help turning it into something publishable.","https:\u002F\u002Fgithub.com\u002FNairon-AI\u002Fagentic-starter-pack\u002Fblob\u002Fmain\u002Fwriting\u002Fwriting-shape\u002FSKILL.md",[71,76],{"categoryNo":72,"categoryName":73,"taxonomyType":74,"taxonomyTypeDesc":75},"01JBSEEDCAT000000000000018","写作与文案","SCENARIO","使用场景",{"categoryNo":77,"categoryName":78,"taxonomyType":79,"taxonomyTypeDesc":80},"01JBSEEDCAT000000000000036","内容创作者","PROFESSION","适用职业",[82,83,84,85,86,87,88,89,90,32,91,92,93,94,95,96,97,98,99,22],"writing","writing shape","shape","notes","help","file","markdown","format","session","take","draft","article","material","it","about","quotes","at","candidate",{"skillNo":101,"name":102,"nameZh":6,"description":103,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":104,"categories":105,"keywords":108,"publishedAt":36},"01M6RBCCCB1KMF01P5SYGFWBSW","writing-fragments","Grilling session that mines the user for fragments — heterogeneous nuggets of writing (claims, vignettes, sharp sentences, half-thoughts) — and appends them to a single document as raw material for a future article. Use when the user wants to develop ideas before imposing structure, or mentions \"fragments\", \"ideate\", or \"raw material\" for writing.","https:\u002F\u002Fgithub.com\u002FNairon-AI\u002Fagentic-starter-pack\u002Fblob\u002Fmain\u002Fwriting\u002Fwriting-fragments\u002FSKILL.md",[106,107],{"categoryNo":72,"categoryName":73,"taxonomyType":74,"taxonomyTypeDesc":75},{"categoryNo":77,"categoryName":78,"taxonomyType":79,"taxonomyTypeDesc":80},[82,109,110,111,112,113,90,32,114,115,116,93,117,94,118,119,120,121,22],"writing fragments","grilling","ideate","develop","user","document","structure","ideas","as","sharp","claims","for","single",{"skillNo":123,"name":124,"nameZh":6,"description":125,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":126,"categories":127,"keywords":130,"publishedAt":36},"01M6RBC8V7NHHKMTAZB4BPXVYQ","writing-beats","Shape an article as a journey of beats, choose-your-own-adventure style. The user picks a starting beat from the raw material, you write only that beat, then offer options for where to pivot next, beat by beat, until the article reaches a natural end. Use when the user has raw material and wants to assemble it as a narrative rather than an argument.","https:\u002F\u002Fgithub.com\u002FNairon-AI\u002Fagentic-starter-pack\u002Fblob\u002Fmain\u002Fwriting\u002Fwriting-beats\u002FSKILL.md",[128,129],{"categoryNo":72,"categoryName":73,"taxonomyType":74,"taxonomyTypeDesc":75},{"categoryNo":77,"categoryName":78,"taxonomyType":79,"taxonomyTypeDesc":80},[82,131,132,133,32,134,135,84,136,137,138,93,139,117,140,94,141,142,120,22],"writing beats","choose","write","assemble","only","journey","next","options","end","style","narrative","argument",{"skillNo":144,"name":145,"nameZh":6,"description":146,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":147,"categories":148,"keywords":151,"publishedAt":36},"01M6RBC58SG5Y5SC4ZV4ZNEFHC","stop-slop","Remove AI writing patterns from prose. Use when drafting, editing, or reviewing text to eliminate predictable AI tells.","https:\u002F\u002Fgithub.com\u002FNairon-AI\u002Fagentic-starter-pack\u002Fblob\u002Fmain\u002Fwriting\u002Fstop-slop\u002FSKILL.md",[149,150],{"categoryNo":72,"categoryName":73,"taxonomyType":74,"taxonomyTypeDesc":75},{"categoryNo":77,"categoryName":78,"taxonomyType":79,"taxonomyTypeDesc":80},[152,153,154,82,155,156,157,158,159,32,160,161,162,120,22],"stop","stop slop","slop","content","https","text","editing","remove","prose","patterns","drafts",{"skillNo":164,"name":165,"nameZh":6,"description":166,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":167,"categories":168,"keywords":171,"publishedAt":36},"01M6RBBJTTCJ7KZCP16G9JDFZH","humanizer","Remove signs of AI-generated writing from text. Use when editing or reviewing\ntext to make it sound more natural and human-written. Based on Wikipedia's\ncomprehensive \"Signs of AI writing\" guide. Detects and fixes patterns including:\ninflated symbolism, promotional language, superficial -ing analyses, vague\nattributions, em dash overuse, rule of three, AI vocabulary words, passive\nvoice, negative parallelisms, and filler phrases.","https:\u002F\u002Fgithub.com\u002FNairon-AI\u002Fagentic-starter-pack\u002Fblob\u002Fmain\u002Fwriting\u002Fhumanizer\u002FSKILL.md",[169,170],{"categoryNo":72,"categoryName":73,"taxonomyType":74,"taxonomyTypeDesc":75},{"categoryNo":77,"categoryName":78,"taxonomyType":79,"taxonomyTypeDesc":80},[165,82,172,173,157,174,133,175,176,159,177,178,161,27,179,180,181,182,183,22],"code","em dash","read","edit","generated","guide","opencode","human","make","wikipedia","vague","dash",{"skillNo":185,"name":186,"nameZh":6,"description":187,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":188,"categories":189,"keywords":190,"publishedAt":36},"01M6RBBEQKJ5HRH8AV9J85QB1T","feedback","Prepare, deliver, and debrief timely interpersonal feedback using a question-first coaching flow, observable behavior, owned experience, and concrete impact. Use when drafting feedback, coaching managers, rehearsing a feedback conversation, praising specific behavior, or reviewing whether feedback landed.","https:\u002F\u002Fgithub.com\u002FNairon-AI\u002Fagentic-starter-pack\u002Fblob\u002Fmain\u002Fwriting\u002Ffeedback\u002FSKILL.md",[],[186,191,192,193,194,195,32,196,197,198,199,200,22,201],"prepare","flow","first","question","behavior","conversation","coaching","debrief","impact","managers","deliver"]