1. Digest
Materials are the visual skin of 3D objects, defining how surfaces interact with light and appear to viewers. This comprehensive chapter explores all major material types and texturing techniques through 17 progressive examples, covering everything from basic material properties to advanced texture workflows.
The journey begins with fundamental material types: MeshBasicMaterial for simple, unlit surfaces; MeshLambertMaterial and MeshPhongMaterial demonstrating the difference between matte and glossy surfaces; and MeshStandardMaterial introducing physically-based rendering with metalness and roughness properties. Flat shading creates faceted, low-poly aesthetics, while side rendering options (FrontSide, BackSide, DoubleSide) control which faces of geometry are visible.
Texture mapping capabilities are thoroughly covered starting with basic texture loading, progressing to managing multiple textures with LoadingManager, and advancing to texture transformations including positioning, rotation, and repetition. Applying different textures to each face of a cube using material arrays with proper texture filtering via magFilter enables pixel-perfect rendering, particularly important for pixelated art styles.
Specialized materials include MeshToonMaterial for cartoon/cel-shaded effects using gradient maps, MeshNormalMaterial for visualizing surface normals with rainbow colors useful for debugging geometry, and MeshMatcapMaterial for achieving complex lighting effects without actual lights by using matcap textures. Combining multiple texture maps (base color, normal, roughness, ambient occlusion) with MeshStandardMaterial creates photorealistic surfaces.
Advanced environmental techniques include environment mapping with cubemaps for realistic reflections on metallic surfaces, creating immersive skyboxes for 360-degree backgrounds, combining skybox and environment mapping for complete environmental integration, and dynamic CanvasTexture for generating procedural textures or displaying real-time content like text and animations directly on 3D surfaces.
This comprehensive coverage provides everything needed to create visually stunning 3D experiences, from simple colored shapes to photorealistic objects with complex surface details, dynamic content, and immersive environments.
2. What is the purpose
The purpose of mastering materials and textures is to transform basic 3D geometry into visually compelling and realistic objects. Understanding different material types enables developers to choose performance-appropriate materials based on project requirements, create diverse visual styles ranging from flat cartoon aesthetics to photorealistic renderings, and implement proper lighting interactions for believable scenes.
Learning texture mapping unlocks the ability to apply detailed surface appearances without complex geometry, implement physically-based rendering workflows using multiple texture maps for realism, create immersive environments through skyboxes and environment maps, and develop dynamic, interactive textures that respond to user input or animation. The knowledge of texture transformations and filtering provides precise control over how images appear on 3D surfaces.
Understanding specialized materials like MeshToonMaterial for stylized rendering, MeshNormalMaterial for debugging geometry issues, and MeshMatcapMaterial for performance-optimized complex appearances expands the creative toolkit. The practical applications extend to creating product visualizations with realistic materials and reflections, building immersive game environments with skyboxes, implementing interactive applications with dynamic canvas-based textures, optimizing render performance by choosing appropriate material types, and achieving specific artistic directions from minimal geometric complexity.
This knowledge is essential for professional 3D web development, enabling developers to make informed decisions about visual quality versus performance trade-offs and implement industry-standard rendering techniques.
3. Some code block and its explanation
Example 1: Material Types Comparison - Basic, Lambert, Phong, and Standard
const basicMaterial = new THREE.MeshBasicMaterial({
color: 'seagreen'
});
const lambertMaterial = new THREE.MeshLambertMaterial({
color: "orange"
});
const phongMaterial = new THREE.MeshPhongMaterial({
color: "orange",
shininess: 1000
});
const standardMaterial = new THREE.MeshStandardMaterial({
color: "orangered",
roughness: 0.1,
metalness: 0.5
});
const flatMaterial = new THREE.MeshStandardMaterial({
color: "orangered",
roughness: 0.1,
metalness: 0.5,
flatShading: true
});
This progression demonstrates the fundamental material types in Three.js, each with distinct characteristics and use cases. MeshBasicMaterial is the simplest and fastest, rendering solid colors without any lighting calculation, making it perfect for UI elements, solid backgrounds, or debugging. It provides no depth perception since it doesn't interact with lights.
MeshLambertMaterial introduces diffuse lighting for matte surfaces like paper, unfinished wood, or concrete, creating basic depth perception through light interaction. MeshPhongMaterial adds specular highlights controlled by the shininess property, ideal for glossy surfaces like plastic, painted metal, or wet surfaces. Higher shininess values create smaller, more focused highlights.
MeshStandardMaterial represents modern physically-based rendering (PBR) using roughness and metalness parameters instead of arbitrary shininess. This approach more accurately simulates real-world materials: roughness controls surface smoothness (0 = mirror-like, 1 = completely rough), while metalness determines electrical conductivity (0 = non-metal like wood or plastic, 1 = pure metal). The flatShading option creates a faceted, low-poly aesthetic by calculating lighting per face rather than per vertex, popular in stylized games and minimalist designs.
Example 2: Side Rendering and Texture Loading
const material = new THREE.MeshStandardMaterial({
color: "orangered",
roughness: 0.2,
metalness: 0.5,
side: THREE.DoubleSide
});
const textureLoader = new THREE.TextureLoader();
const texture = textureLoader.load(
"/textures/brick/Wood_Wicker_012_ambientOcclusion.png",
() => console.log("Load complete"),
() => console.log("Loading..."),
() => console.log("Load error")
);
const textureMaterial = new THREE.MeshStandardMaterial({
map: texture
});
const loadingManager = new THREE.LoadingManager();
loadingManager.onStart = () => console.log("Loading started");
loadingManager.onProgress = (img) => console.log(img + " loading...");
loadingManager.onLoad = () => console.log("All textures loaded");
loadingManager.onError = (img) => console.log(img + " error");
const loader = new THREE.TextureLoader(loadingManager);
const baseColorTexture = loader.load("/textures/brick/Wood_Wicker_012_basecolor.png");
const normalTexture = loader.load("/textures/brick/Wood_Wicker_012_normal.png");
const roughnessTexture = loader.load("/textures/brick/Wood_Wicker_012_roughness.png");
const ambientTexture = loader.load("/textures/brick/Wood_Wicker_012_ambientOcclusion.png");
The side property controls which faces of geometry are rendered. The default THREE.FrontSide only renders the outside faces (determined by vertex winding order), providing best performance. THREE.BackSide renders only inside faces, useful for creating interior environments or special effects. THREE.DoubleSide renders both faces, necessary for thin objects like planes, leaves, or cloth that should be visible from both sides, though it doubles the rendering cost.
Texture loading uses THREE.TextureLoader with optional callbacks for success, progress, and error handling. The basic approach works for single textures but becomes cumbersome with multiple images. LoadingManager solves this by centralizing load event handling across multiple textures, providing unified callbacks for tracking overall loading progress. This is essential for displaying loading screens, preloading assets, and ensuring all textures are ready before rendering. The manager-based approach scales better for complex scenes with dozens or hundreds of textures.
Example 3: Texture Transformation and Multi-Material Geometry
const skullTexture = textureLoader.load("/textures/skull/Ground_Skull_basecolor.jpg");
skullTexture.wrapS = THREE.RepeatWrapping;
skullTexture.wrapT = THREE.RepeatWrapping;
skullTexture.offset.x = 0.3;
skullTexture.offset.y = 0.2;
skullTexture.repeat.x = 2;
skullTexture.repeat.y = 3;
skullTexture.rotation = Math.PI / 4;
skullTexture.center.x = 0.5;
skullTexture.center.y = 0.5;
const materials = [
new THREE.MeshBasicMaterial({ map: rightTexture }),
new THREE.MeshBasicMaterial({ map: leftTexture }),
new THREE.MeshBasicMaterial({ map: topTexture }),
new THREE.MeshBasicMaterial({ map: bottomTexture }),
new THREE.MeshBasicMaterial({ map: frontTexture }),
new THREE.MeshBasicMaterial({ map: backTexture })
];
rightTexture.magFilter = THREE.NearestFilter;
const mesh = new THREE.Mesh(geometry, materials);
Texture transformation provides powerful control over how images map onto geometry. The wrapS and wrapT properties determine behavior beyond the standard 0-1 UV coordinate range: RepeatWrapping creates seamless tiling patterns, ClampToEdgeWrapping stretches edge pixels, and MirroredRepeatWrapping mirrors the texture at boundaries. The offset property shifts the texture position, useful for animating textures (like scrolling water) or fine-tuning alignment. The repeat property scales the texture, with values less than 1 magnifying it and values greater than 1 creating multiple tiles. Rotation transforms occur around a pivot point defined by center (default is bottom-left corner at 0,0).
The multi-material approach allows different textures on each face of a cube (or any geometry with face groups), perfect for Minecraft-style blocks, dice, or skyboxes. The materials array order corresponds to geometry faces in a specific sequence. The magFilter property controls texture magnification filtering: THREE.LinearFilter (default) smooths pixels when scaled up, while THREE.NearestFilter preserves sharp pixel boundaries, essential for pixel art, retro aesthetics, or maintaining crisp details in low-resolution textures.
Example 4: Specialized Materials - Toon, Normal, and Matcap
const gradientTexture = textureLoader.load("/textures/gradient.png");
gradientTexture.magFilter = THREE.NearestFilter;
const toonMaterial = new THREE.MeshToonMaterial({
color: 'plum',
gradientMap: gradientTexture
});
const normalMaterial = new THREE.MeshNormalMaterial();
const matcapTexture = textureLoader.load("/textures/matcap/material3.jpg");
const matcapMaterial = new THREE.MeshMatcapMaterial({
matcap: matcapTexture
});
MeshToonMaterial creates cartoon or cel-shaded aesthetics popular in anime-styled games and non-photorealistic rendering. By default, it produces a simple two-tone effect (lit and shadowed areas). The gradientMap allows customizing the number and colors of shading bands by providing a gradient texture, with NearestFilter ensuring sharp transitions between color bands rather than smooth gradients. This technique is fundamental for stylized games like The Legend of Zelda: Wind Waker or Genshin Impact.
MeshNormalMaterial encodes surface normal vectors as RGB colors, where each axis (X, Y, Z) maps to a color channel (Red, Green, Blue). This creates a characteristic rainbow appearance that's invaluable for debugging geometry issues like incorrect normals, inside-out faces, or smoothing problems. It's also used artistically for psychedelic or diagnostic visualization styles. Since normals are view-independent, the colors change as you rotate the camera around objects.
MeshMatcapMaterial achieves sophisticated lighting effects without actual lights by using a special "material capture" texture, a photograph of a sphere with desired lighting and material properties. The material looks up colors based on surface normals, creating an illusion of complex lighting and reflections. This is extremely performant since there's no real-time lighting calculation, making it ideal for mobile applications, large scenes, or when you want a specific pre-defined look that's difficult to achieve with real lights.
Example 5: Advanced Texturing - Multiple Maps and Environment
const material = new THREE.MeshStandardMaterial({
map: baseColorTexture,
normalMap: normalTexture,
roughnessMap: roughnessTexture,
aoMap: ambientTexture,
aoMapIntensity: 4,
roughness: 0.3,
metalness: 0.1
});
const cubeTextureLoader = new THREE.CubeTextureLoader();
const envTexture = cubeTextureLoader.setPath("textures/cubemap/").load([
"px.png", "nx.png",
"py.png", "ny.png",
"pz.png", "nz.png"
]);
const reflectiveMaterial = new THREE.MeshStandardMaterial({
envMap: envTexture,
metalness: 2,
roughness: 0.1
});
scene.background = cubeTextureLoader.setPath("textures/cubemap/").load([
"px.png", "nx.png", "py.png", "ny.png", "pz.png", "nz.png"
]);
const cubeTexture = cubeTextureLoader.setPath("textures/cubemap/").load([
"px.png", "nx.png", "py.png", "ny.png", "pz.png", "nz.png"
]);
scene.background = cubeTexture;
const material = new THREE.MeshBasicMaterial({
envMap: cubeTexture
});
Multiple texture maps are the foundation of photorealistic rendering. The base color map (map) provides fundamental appearance, the normal map adds fine surface detail like bumps, scratches, and grain without additional geometry (a performance optimization), the roughness map varies surface smoothness across the object (think polished metal with scratched areas), and the ambient occlusion (AO) map enhances depth perception by darkening crevices, corners, and contact points where ambient light would naturally be occluded. The aoMapIntensity multiplier controls how pronounced these shadows are. Base roughness and metalness values multiply with their respective maps if present.
Environment mapping creates realistic reflections by using a cubemap - six square textures representing all directions from a point in space. The images must follow a specific order (positive/negative X, Y, Z faces). When applied as envMap, the material reflects the surrounding environment, with appearance controlled by metalness (how mirror-like) and roughness (how blurred the reflections). This is essential for metals, glass, water, and any reflective surface.
Skyboxes use cubemaps as scene backgrounds, creating immersive 360-degree environments that surround the entire scene. When combining skybox and environment mapping, objects naturally reflect the same environment visible in the background, creating visual coherence where metallic or glossy objects mirror their surroundings convincingly. This technique is fundamental to photorealistic rendering and is widely used in games, product visualization, and architectural visualization.
Example 6: Dynamic Canvas Textures
const textureCanvas = document.createElement('canvas');
const textureContext = textureCanvas.getContext('2d');
textureCanvas.width = 500;
textureCanvas.height = 500;
const canvasTexture = new THREE.CanvasTexture(textureCanvas);
const material = new THREE.MeshBasicMaterial({
map: canvasTexture
});
function draw() {
const time = clock.getElapsedTime();
material.map.needsUpdate = true;
textureContext.fillStyle = 'blue';
textureContext.fillRect(0, 0, 500, 500);
textureContext.fillStyle = 'white';
textureContext.fillRect(time * 50, 100, 50, 50);
textureContext.font = 'bold 45px sans-serif';
textureContext.fillText('I am a canvas texture', 10, 400);
renderer.render(scene, camera);
requestAnimationFrame(draw);
}
CanvasTexture bridges HTML5 Canvas API with Three.js textures, enabling dynamic, procedurally generated content on 3D surfaces. By creating a 2D canvas and wrapping it in a CanvasTexture, you can use all standard canvas drawing operations (shapes, gradients, text, images) and have them appear on 3D geometry. The key requirement is setting material.map.needsUpdate = true in the animation loop whenever canvas content changes, telling Three.js to upload the updated pixel data to the GPU.
This technique unlocks numerous applications: displaying live data visualizations on 3D dashboards, creating animated UI elements in 3D space, rendering real-time text or user-generated content, implementing procedural textures without image files, building interactive surfaces that respond to user input, generating noise or particle effects, and even displaying video content. It's particularly powerful for applications requiring dynamic content like games with HUD elements, educational visualizations with changing labels, or interactive art installations. The performance trade-off is the CPU cost of canvas operations and GPU texture uploads each frame, so keep canvas resolution reasonable for real-time applications.