Textures are uploaded per material slot, loaded through Three.js TextureLoader, cached by URL and color-managed according to the map type. Getting the color space wrong is the most common source of incorrect-looking materials.
| Map Type | Color Space | Why |
|---|---|---|
| Color (Albedo) | sRGB | Color data is authored in sRGB. Three.js needs to know this to linearize it correctly before lighting calculations. |
| Emissive Map | sRGB | Emissive colors are authored in sRGB for the same reason. |
| All other maps | Linear | Roughness, metalness, normal, bump, AO and alpha maps store non-color data — physical parameters or vectors. Loaded as sRGB, the gamma curve would distort the values. |
| Control | Three.js Property | Visual Effect |
|---|---|---|
| Repeat X/Y | texture.repeat | Tiles the texture. Values above 1 repeat it, below 1 stretch it |
| Offset X/Y | texture.offset | Slides the texture across the surface |
| Rotation | texture.rotation | Rotates the texture around the UV origin |
| Wrap Mode | texture.wrapS / wrapT | Clamp stretches edge pixels; Repeat tiles seamlessly; Mirror tiles with alternating flip |
| Anisotropy | texture.anisotropy | Improves clarity at oblique angles. 1 is none, 16 is maximum, and higher costs more GPU |
const wrapMap = {
clamp: THREE.ClampToEdgeWrapping,
repeat: THREE.RepeatWrapping,
mirror: THREE.MirroredRepeatWrapping,
};Three.js MeshStandardMaterial reads AO maps from a second UV channel (uv2). The current code applies AO maps without explicitly creating a uv2 attribute. For primitives created by Three.js the default UV coordinates serve both channels, so this works — but imported models with a separate UV2 layout may show incorrect ambient occlusion.
Removing a texture from a slot calls disposeTextureByUrl(), which disposes the GPU texture and deletes the cache entry. Textures are not released by SceneManager.removeObject() — that method handles geometries and materials only, so texture cleanup is a separate responsibility.