[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill-48ae958d-1ec4-4777-8e1e-e739234b9a57":3,"$f7IkBm3K5yqdEF_-8nPM16bPYOgoiSZxTZzre3gKKB9c":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},"48ae958d-1ec4-4777-8e1e-e739234b9a57","threejs-textures","Three.js纹理 - 纹理类型、UV贴图、环境贴图、纹理设置。用于处理图像、UV坐标、立方体贴图、HDR环境或纹理优化时使用。","cat_life_career","mod_other","sickn33,other","---\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.\nrisk: unknown\nsource: community\n---\n\n# Three.js Textures\n\n## When to Use\n- You need to load, configure, or optimize textures in Three.js.\n- The task involves UV mapping, texture settings, cubemaps, environment maps, or HDR texture workflows.\n- You are working on surface detail and material inputs rather than geometry or animation.\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## KTX2Loader BC3 Alpha Fix (r183)\n\nAs of r183, `KTX2Loader` correctly handles BC3 compressed textures with alpha channels, fixing previously incorrect alpha rendering.\n\n## ISO 21496-1 Gainmap Metadata (r183)\n\nThree.js r183 supports ISO 21496-1 gainmap metadata in HDR textures, enabling proper tone mapping of gainmap-based HDR images (such as those produced by recent smartphone cameras).\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\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,142,1664,"2026-05-16 13:44:25",{"id":8,"name":21,"slug":22,"icon":23,"description":24,"sort":25,"createdAt":26},"其他","other","mdi-page-next-outline","其他类型Skill",5,"2026-05-16 12:53:40",{"id":7,"name":28,"slug":29,"icon":30,"description":31,"moduleId":8,"sort":32,"skillCount":33,"createdAt":26},"职场发展","career","mdi-briefcase-outline","面试准备、简历优化、职业规划",4,575,[35],{"id":36,"skillId":4,"version":37,"fileName":38,"fileSize":39,"filePath":40,"fileHash":41,"manifest":42,"createdAt":19},"0283909c-c5c7-4948-b5a1-298f009b8bdf","1.0.0","threejs-textures.zip",4999,"uploads\u002Fskills\u002F48ae958d-1ec4-4777-8e1e-e739234b9a57\u002Fthreejs-textures.zip","2be10fa83400299b14113e17fbcd89524ecf4a25fc44f3fa20e412a64d426b49","[{\"path\":\"SKILL.md\",\"isDirectory\":false,\"size\":14662}]",{"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]