Three.js - Textures
Three.js - Textures
Section titled “Three.js - Textures”A texture is an image that is mapped to the surface of a 3D model to add color, detail, and realism. In modern 3D graphics, especially with Physically Based Rendering (PBR), textures do more than just add color—they define properties like roughness, metalness, and surface height. This chapter covers how to load and apply these powerful maps.
Loading a Basic Texture
Section titled “Loading a Basic Texture”The foundation of texturing is loading an image file. Three.js provides TextureLoader for this purpose. The loaded texture can then be applied to a material’s map property, which corresponds to the base color.
import * as THREE from 'three';
// 1. Create a TextureLoader instanceconst textureLoader = new THREE.TextureLoader();
// 2. Load an image fileconst colorTexture = textureLoader.load('/textures/door/color.jpg');
// 3. Apply it to a materialconst material = new THREE.MeshStandardMaterial({ map: colorTexture });Loading is asynchronous. The texture might not be visible immediately. Three.js handles this in the background, but for more complex scenarios, you can use the loader’s callback functions to know when loading is complete.
Configuring Textures: Tiling and Filtering
Section titled “Configuring Textures: Tiling and Filtering”You can control how a texture repeats (tiles) and how it looks when scaled up or down.
- Wrapping (Tiling): Use
texture.wrapS(horizontal) andtexture.wrapT(vertical) withTHREE.RepeatWrappingto make a texture tile across a surface. You can then set how many times it repeats withtexture.repeat.set(x, y). - Filtering: When a texture is stretched over a large area (
magFilter) or shrunk onto a small area (minFilter), filtering determines how pixels are rendered.THREE.LinearFilterprovides a smooth look, whileTHREE.NearestFiltergives a sharp, pixelated look. For best quality withminFilter, use one of the Mipmap options likeTHREE.LinearMipmapLinearFilter.
// Make the texture repeat 4 times horizontally and 2 times verticallycolorTexture.wrapS = THREE.RepeatWrapping;colorTexture.wrapT = THREE.RepeatWrapping;colorTexture.repeat.set(4, 2);
// For pixel art or sharp texturescolorTexture.magFilter = THREE.NearestFilter;
// Set colorspace for PBR materialscolorTexture.colorSpace = THREE.SRGBColorSpace;Important: For PBR materials like MeshStandardMaterial, it is crucial to set the correct colorSpace for textures that contain color information (like the map). THREE.SRGBColorSpace is the correct choice for most color textures.
PBR Texture Maps for Realism
Section titled “PBR Texture Maps for Realism”MeshStandardMaterial simulates realistic lighting by using several types of texture maps. These are typically grayscale or special-purpose images that control specific physical properties of the surface.
- Normal Map (
.normalMap): Adds fine surface detail like bumps, scratches, and grooves without adding more polygons. It fakes detail by manipulating how light reflects off the surface. This is more advanced than a bump map. - Ambient Occlusion (AO) Map (
.aoMap): A grayscale map that adds fake shadows in crevices and corners where light is blocked. It makes objects look more grounded and less flat. This requires a second set of UV coordinates (uv2). - Roughness Map (
.roughnessMap): A grayscale map where white means a very rough surface (diffuse reflection, like chalk) and black means a very smooth surface (sharp reflection, like a mirror). - Metalness Map (
.metalnessMap): A grayscale map where white indicates the surface is 100% metallic and black indicates it is non-metallic (dielectric). - Displacement Map (
.displacementMap): Unlike a normal map which only fakes detail, a displacement map actually moves the vertices of the geometry. This can create true geometric detail but is computationally expensive and requires a highly subdivided mesh.
Example: Applying a Full PBR Texture Set
Section titled “Example: Applying a Full PBR Texture Set”This example demonstrates applying a full set of PBR textures to a sphere. It assumes you have a set of textures (color, normal, roughness, etc.) in a /textures/ folder in your public directory. You can find free PBR texture sets on sites like AmbientCG.
import * as THREE from 'three';import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
// Base Setup (Scene, Camera, Renderer, Controls as in previous examples)// ...
/** * Textures */const textureLoader = new THREE.TextureLoader();
// Load all the required PBR mapsconst colorTexture = textureLoader.load('/textures/metal_plate/color.jpg');const aoTexture = textureLoader.load('/textures/metal_plate/ambientOcclusion.jpg');const heightTexture = textureLoader.load('/textures/metal_plate/height.png');const normalTexture = textureLoader.load('/textures/metal_plate/normal.jpg');const roughnessTexture = textureLoader.load('/textures/metal_plate/roughness.jpg');const metalnessTexture = textureLoader.load('/textures/metal_plate/metalness.jpg');
// Set the correct color space for the color texturecolorTexture.colorSpace = THREE.SRGBColorSpace;
/** * Objects */// Use a highly detailed geometry to see the effects of displacementconst geometry = new THREE.SphereGeometry(1, 64, 64);
// Add the aoMap's UV attribute. It shares the same UVs as the color map.geometry.setAttribute('uv2', new THREE.BufferAttribute(geometry.attributes.uv.array, 2));
const material = new THREE.MeshStandardMaterial({ map: colorTexture, aoMap: aoTexture, aoMapIntensity: 1, normalMap: normalTexture, roughnessMap: roughnessTexture, metalnessMap: metalnessTexture, displacementMap: heightTexture, displacementScale: 0.05 // Adjust this value carefully});
const sphere = new THREE.Mesh(geometry, material);scene.add(sphere);
/** * Lights */const ambientLight = new THREE.AmbientLight(0xffffff, 0.5);scene.add(ambientLight);
const pointLight = new THREE.PointLight(0xffffff, 20);pointLight.position.set(2, 3, 4);scene.add(pointLight);
// ... (Animation loop and responsiveness as in previous examples)Texture Optimization Best Practices
Section titled “Texture Optimization Best Practices”Textures are often the largest part of a 3D application’s file size. Optimizing them is critical for fast loading times.
- File Format: Use
.jpgfor color and other maps without transparency. Use.pngonly when you need an alpha channel. - Resolution: Use the smallest resolution that still looks good. Keep dimensions to a power of two (e.g., 512x512, 1024x1024) for broad compatibility and mipmapping efficiency.
- Texture Compression: For production, use tools like Basis Universal to create super-compressed
.basisor.ktx2files that can be decoded efficiently by the GPU, significantly reducing memory usage and load times.