Every material extends Material, which holds Depth, Culling, Blending's render state: side, transparent, opacity, blending, depthTest, depthWrite and alphaTest. The mesh materials differ in their lighting model:
| Material | Lighting | Use it for |
|---|---|---|
| MeshBasicMaterial | None | Unlit UI, baked lighting, wireframes |
| MeshLambertMaterial | Diffuse only, per fragment since r144 | Cheap matte surfaces |
| MeshPhongMaterial | Blinn-Phong (Blinn-Phong) | Legacy shiny surfaces |
| MeshToonMaterial | Stepped by a gradientMap | Cartoon shading |
| MeshStandardMaterial | Metalness-roughness PBR (Metallic-Roughness) | The default choice |
| MeshPhysicalMaterial | PBR plus clearcoat, transmission, sheen | Glass, car paint, fabric |
| MeshNormalMaterial, MeshDepthMaterial | Debug output | Checking normals and depth |
| ShaderMaterial | Your GLSL (ShaderMaterial and TSL) | Anything else |
Share one material among many meshes. Changing a feature that alters the shader (adding a map, toggling vertexColors) needs material.needsUpdate = true so the program is rebuilt.
<!doctype html>
<style>
body { margin: 0; background: #f5f1e8; font: 12px system-ui, sans-serif; color: #263238; }
#wrap { position: relative; width: 600px; max-width: 100%; }
canvas { display: block; max-width: 100%; }
.tag { position: absolute; width: 150px; text-align: center; }
</style>
<script type="importmap">
{ "imports": {
"three": "https://cdn.jsdelivr.net/npm/three@0.186.1/build/three.module.js",
"three/addons/": "https://cdn.jsdelivr.net/npm/three@0.186.1/examples/jsm/" } }
</script>
<div id="wrap"></div>
<script type="module">
import * as THREE from 'three';
const wrap = document.getElementById('wrap');
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setPixelRatio(Math.min(devicePixelRatio, 2));
renderer.setSize(600, 320);
wrap.appendChild(renderer.domElement);
const scene = new THREE.Scene();
scene.background = new THREE.Color('#fbf8f2');
const camera = new THREE.OrthographicCamera(-4, 4, 2.13, -2.13, 0.1, 20);
camera.position.set(0, 0, 8);
scene.add(new THREE.HemisphereLight('#ffffff', '#8d6e63', 0.8));
const sun = new THREE.DirectionalLight('#ffffff', 2.5);
sun.position.set(2, 3, 4);
scene.add(sun);
// A small gradient for MeshToonMaterial: three flat bands of light
const bands = new THREE.DataTexture(new Uint8Array([60, 160, 255]), 3, 1, THREE.RedFormat);
bands.minFilter = bands.magFilter = THREE.NearestFilter;
bands.needsUpdate = true;
const color = '#e09a10';
const materials = [
['MeshBasicMaterial', new THREE.MeshBasicMaterial({ color })], // no lighting
['MeshLambertMaterial', new THREE.MeshLambertMaterial({ color })], // diffuse only
['MeshPhongMaterial', new THREE.MeshPhongMaterial({ color, shininess: 80 })],
['MeshToonMaterial', new THREE.MeshToonMaterial({ color, gradientMap: bands })],
['MeshStandardMaterial', new THREE.MeshStandardMaterial({ color, roughness: 0.35, metalness: 0.2 })],
['MeshPhysicalMaterial', new THREE.MeshPhysicalMaterial({ color, roughness: 0.5, clearcoat: 1, clearcoatRoughness: 0.05 })],
['MeshNormalMaterial', new THREE.MeshNormalMaterial()], // debug: normals as colors
['MeshDepthMaterial', new THREE.MeshDepthMaterial()], // debug: distance
];
const geometry = new THREE.TorusKnotGeometry(0.4, 0.13, 128, 24);
const knots = materials.map(([name, material], i) => {
const knot = new THREE.Mesh(geometry, material); // one geometry shared by all eight
const col = i % 4, row = Math.floor(i / 4);
knot.position.set(-3 + col * 2, 0.95 - row * 2, 0);
scene.add(knot);
const tag = wrap.appendChild(document.createElement('div'));
tag.className = 'tag';
tag.textContent = name;
tag.style.left = `${col * 150}px`;
tag.style.top = `${row * 160 + 142}px`;
return knot;
});
// Depth material maps near..far to white..black: tighten the camera range so it shows
camera.near = 7; camera.far = 9; camera.updateProjectionMatrix();
renderer.setAnimationLoop((time) => {
knots.forEach((k) => { k.rotation.set(time / 3000, time / 2000, 0); });
renderer.render(scene, camera);
});
</script>