# Understanding Three.js Materials: From Basic to Physical

Welcome back! 👋 In my previous posts, I covered the [basics of Three.js](https://blog.iamdipankarpaul.com/my-first-threejs-scene-building-a-3d-world-in-the-browser) and [different geometries](https://blog.iamdipankarpaul.com/exploring-threejs-geometries-the-building-blocks-of-3d). Today, I'm exploring **materials** - what makes objects look the way they do.

If geometries are the shape of an object, materials are its appearance - the colour, shininess, transparency and how it reacts to light. Let's dive in!

## What Are Materials?

In Three.js, materials define how the surface of a geometry looks and behaves. They control:

* **Colour and appearance** (matte, shiny, metallic)
    
* **Light interaction** (does it need lights to be visible?)
    
* **Transparency** (can you see through it?)
    
* **Special effects** (glowing, wireframe mode)
    

Different materials have different performance costs and visual capabilities. Choosing the right material is about balancing looks and performance.

## Project Setup

Here's our basic HTML structure:

```html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Three.js Materials Gallery</title>
    <style>
        body {
            margin: 0;
            overflow: hidden;
            font-family: Arial, sans-serif;
        }
        canvas {
            display: block;
        }
        #controls {
            position: absolute;
            top: 20px;
            right: 20px;
            color: white;
            background: rgba(0, 0, 0, 0.8);
            padding: 20px;
            border-radius: 8px;
            font-size: 14px;
            max-width: 300px;
        }
        #controls h3 {
            margin-top: 0;
            color: #4ecdc4;
        }
        .control-group {
            margin: 15px 0;
        }
        .control-group label {
            display: block;
            margin-bottom: 5px;
            color: #aaa;
        }
        input[type="range"] {
            width: 100%;
        }
        button {
            background: #4ecdc4;
            color: black;
            border: none;
            padding: 8px 15px;
            border-radius: 4px;
            cursor: pointer;
            margin: 5px 5px 5px 0;
            font-weight: bold;
        }
        button:hover {
            background: #45b8af;
        }
    </style>
</head>
<body>
    <canvas id="canvas"></canvas>
    <div id="controls">
        <h3>Material Properties</h3>
        <div class="control-group">
            <label>Metalness: <span id="metalnessValue">0.5</span></label>
            <input type="range" id="metalness" min="0" max="1" step="0.1" value="0.5">
        </div>
        <div class="control-group">
            <label>Roughness: <span id="roughnessValue">0.5</span></label>
            <input type="range" id="roughness" min="0" max="1" step="0.1" value="0.5">
        </div>
        <div class="control-group">
            <label>Opacity: <span id="opacityValue">1.0</span></label>
            <input type="range" id="opacity" min="0" max="1" step="0.1" value="1">
        </div>
        <div class="control-group">
            <button id="toggleWireframe">Toggle Wireframe</button>
            <button id="toggleEmissive">Toggle Glow</button>
        </div>
    </div>
    <script type="module" src="script.js"></script>
</body>
</html>
```

## Building Our Materials Gallery

Let's create a scene with the same sphere shape using five different materials to see how they compare!

### Step 1: Scene and Camera Setup

```jsx
import * as THREE from "three";

const canvas = document.getElementById("canvas");

// Scene setup
const scene = new THREE.Scene();
scene.background = new THREE.Color(0x1a1a2e); // Dark blue background

// Camera setup
const camera = new THREE.PerspectiveCamera(
    75,
    window.innerWidth / window.innerHeight,
    0.1,
    1000
);
camera.position.set(0, 0, 15); // Pull back to see all spheres
camera.lookAt(0, 0, 0);
```

### Step 2: Lighting Setup

```jsx
// Ambient light - base illumination for all objects
const ambientLight = new THREE.AmbientLight(0xffffff, 0.3);
scene.add(ambientLight);

// Directional light - main light source (like sunlight)
const directionalLight = new THREE.DirectionalLight(0xffffff, 1);
directionalLight.position.set(5, 5, 5);
scene.add(directionalLight);

// Point light - adds dynamic colored lighting
const pointLight = new THREE.PointLight(0xff00ff, 1);
pointLight.position.set(-5, 3, 5);
scene.add(pointLight);

// Another point light on the opposite side
const pointLight2 = new THREE.PointLight(0x00ffff, 0.8);
pointLight2.position.set(5, -3, 5);
scene.add(pointLight2);
```

### Step 3: Creating Materials

Now let's create our five different materials!

```jsx
// ============================================
// 1. MESH BASIC MATERIAL
// Simplest material - NO lighting calculation
// ============================================
const basicMaterial = new THREE.MeshBasicMaterial({
    color: 0xff6b6b,           // Red color
    wireframe: false           // Show as solid (not wireframe)
});

// Use case: UI elements, backgrounds, objects that should
// always be visible regardless of lighting

// ============================================
// 2. MESH LAMBERT MATERIAL
// Matte (non-shiny) surfaces with lighting
// ============================================
const lambertMaterial = new THREE.MeshLambertMaterial({
    color: 0x4ecdc4,           // Cyan color
    emissive: 0x000000,        // No glow by default
    wireframe: false
});

// Use case: Matte surfaces like paper, unpolished wood,
// fabric, chalk. Good performance for non-reflective objects.

// ============================================
// 3. MESH PHONG MATERIAL
// Shiny surfaces with specular highlights
// ============================================
const phongMaterial = new THREE.MeshPhongMaterial({
    color: 0xffe66d,           // Yellow color
    shininess: 100,            // How shiny (0-100+)
    specular: 0xffffff,        // Color of the shine
    emissive: 0x000000,
    wireframe: false
});

// Use case: Plastic, polished surfaces, glossy paint.
// Creates visible light reflections (specular highlights).

// ============================================
// 4. MESH STANDARD MATERIAL
// Physically Based Rendering (PBR) - realistic materials
// ============================================
const standardMaterial = new THREE.MeshStandardMaterial({
    color: 0xa8e6cf,           // Mint green
    metalness: 0.5,            // How metallic (0 = non-metal, 1 = full metal)
    roughness: 0.5,            // Surface roughness (0 = smooth, 1 = rough)
    emissive: 0x000000,
    wireframe: false
});

// Use case: Most modern 3D applications. Provides realistic
// materials using metalness/roughness workflow. Good for
// metals, plastics, and general realistic rendering.

// ============================================
// 5. MESH PHYSICAL MATERIAL
// Advanced PBR with extra features
// ============================================
const physicalMaterial = new THREE.MeshPhysicalMaterial({
    color: 0xffa8e8,           // Pink
    metalness: 0.8,
    roughness: 0.2,
    clearcoat: 1.0,            // Clear coating layer (like car paint)
    clearcoatRoughness: 0.1,   // Roughness of the clear coat
    reflectivity: 1.0,         // How reflective
    emissive: 0x000000,
    wireframe: false
});

// Use case: Car paint, gems, high-quality materials that need
// extra realism. Most expensive performance-wise but most realistic.
```

## Understanding Material Properties

Let me break down the key properties you'll use most often:

### Common Properties (All Materials)

**Colour** - The base colour of the material

```jsx
material.color = new THREE.Color(0xff0000); // Red
```

**opacity** - Transparency level (0 = invisible, 1 = solid)

```jsx
material.transparent = true; // Must enable this first
material.opacity = 0.5; // 50% transparent
```

**wireframe** - Shows the geometry's triangle structure

```jsx
material.wireframe = true; // Great for debugging!
```

**visible** - Whether the material renders at all

```jsx
material.visible = false; // Hide the object
```

### Lighting-Based Properties

**emissive** - Makes material glow (like it has its own light)

```jsx
material.emissive = new THREE.Color(0x00ff00); // Green glow
```

**emissiveIntensity** - How strong the glow is

```jsx
material.emissiveIntensity = 0.5; // 50% glow strength
```

### PBR Properties (Standard & Physical Materials)

**metalness** - How metallic the surface is (0-1)

* 0 = Non-metal (plastic, wood, rubber)
    
* 1 = Full metal (gold, steel, copper)
    

**roughness** - How rough/smooth the surface is (0-1)

* 0 = Mirror-smooth (polished metal)
    
* 1 = Very rough (concrete, rough wood)
    

### Physical Material Extras

**clearcoat** - Adds a glossy layer on top (like car paint)

```jsx
material.clearcoat = 1.0; // Full clear coat
material.clearcoatRoughness = 0.1; // Slightly rough coating
```

**transmission** - How much light passes through (glass effect)

```jsx
material.transmission = 1.0; // Fully transparent to light
```

### Step 4: Creating Spheres with Different Materials

```jsx
// Create base geometry (we'll reuse this for all spheres)
const sphereGeometry = new THREE.SphereGeometry(1.5, 64, 64);
// Using 64 segments for smooth appearance

// Create 5 spheres, each with a different material
const basicSphere = new THREE.Mesh(sphereGeometry, basicMaterial);
basicSphere.position.set(-8, 2, 0);
scene.add(basicSphere);

const lambertSphere = new THREE.Mesh(sphereGeometry, lambertMaterial);
lambertSphere.position.set(-4, 2, 0);
scene.add(lambertSphere);

const phongSphere = new THREE.Mesh(sphereGeometry, phongMaterial);
phongSphere.position.set(0, 2, 0);
scene.add(phongSphere);

const standardSphere = new THREE.Mesh(sphereGeometry, standardMaterial);
standardSphere.position.set(4, 2, 0);
scene.add(standardSphere);

const physicalSphere = new THREE.Mesh(sphereGeometry, physicalMaterial);
physicalSphere.position.set(8, 2, 0);
scene.add(physicalSphere);

// Store references for interactive controls
const spheres = [basicSphere, lambertSphere, phongSphere, standardSphere, physicalSphere];
const materials = [basicMaterial, lambertMaterial, phongMaterial, standardMaterial, physicalMaterial];
```

### Step 5: Adding Labels

Let's add text labels so we know which material is which:

```jsx
// Create text labels using canvas textures
function createTextLabel(text) {
    const canvas = document.createElement('canvas');
    const context = canvas.getContext('2d');
    canvas.width = 512;
    canvas.height = 128;

    context.fillStyle = '#000000';
    context.fillRect(0, 0, canvas.width, canvas.height);

    context.font = 'Bold 48px Arial';
    context.fillStyle = '#ffffff';
    context.textAlign = 'center';
    context.fillText(text, canvas.width / 2, canvas.height / 2 + 16);

    const texture = new THREE.CanvasTexture(canvas);
    const spriteMaterial = new THREE.SpriteMaterial({ map: texture });
    const sprite = new THREE.Sprite(spriteMaterial);
    sprite.scale.set(4, 1, 1);

    return sprite;
}

// Add labels below each sphere
const labels = [
    { text: 'Basic', position: [-8, 0, 0] },
    { text: 'Lambert', position: [-4, 0, 0] },
    { text: 'Phong', position: [0, 0, 0] },
    { text: 'Standard', position: [4, 0, 0] },
    { text: 'Physical', position: [8, 0, 0] }
];

labels.forEach(label => {
    const sprite = createTextLabel(label.text);
    sprite.position.set(label.position[0], label.position[1], label.position[2]);
    scene.add(sprite);
});
```

### Step 6: Interactive Controls

Now let's add interactivity so we can adjust material properties in real-time!

```jsx
// Get control elements
const metalnessSlider = document.getElementById('metalness');
const roughnessSlider = document.getElementById('roughness');
const opacitySlider = document.getElementById('opacity');
const wireframeBtn = document.getElementById('toggleWireframe');
const emissiveBtn = document.getElementById('toggleEmissive');

const metalnessValue = document.getElementById('metalnessValue');
const roughnessValue = document.getElementById('roughnessValue');
const opacityValue = document.getElementById('opacityValue');

// Metalness control (only affects Standard and Physical materials)
metalnessSlider.addEventListener('input', (e) => {
    const value = parseFloat(e.target.value);
    metalnessValue.textContent = value.toFixed(1);

    // Only Standard and Physical materials have metalness
    standardMaterial.metalness = value;
    physicalMaterial.metalness = value;
});

// Roughness control (only affects Standard and Physical materials)
roughnessSlider.addEventListener('input', (e) => {
    const value = parseFloat(e.target.value);
    roughnessValue.textContent = value.toFixed(1);

    standardMaterial.roughness = value;
    physicalMaterial.roughness = value;
});

// Opacity control (affects all materials)
opacitySlider.addEventListener('input', (e) => {
    const value = parseFloat(e.target.value);
    opacityValue.textContent = value.toFixed(1);

    // Enable transparency and set opacity for all materials
    materials.forEach(material => {
        material.transparent = value < 1.0;
        material.opacity = value;
    });
});

// Wireframe toggle
let wireframeEnabled = false;
wireframeBtn.addEventListener('click', () => {
    wireframeEnabled = !wireframeEnabled;
    materials.forEach(material => {
        material.wireframe = wireframeEnabled;
    });
});

// Emissive (glow) toggle
let emissiveEnabled = false;
emissiveBtn.addEventListener('click', () => {
    emissiveEnabled = !emissiveEnabled;

    // Basic material doesn't have emissive property
    lambertMaterial.emissive.setHex(emissiveEnabled ? 0x002222 : 0x000000);
    phongMaterial.emissive.setHex(emissiveEnabled ? 0x222200 : 0x000000);
    standardMaterial.emissive.setHex(emissiveEnabled ? 0x002200 : 0x000000);
    physicalMaterial.emissive.setHex(emissiveEnabled ? 0x220022 : 0x000000);
});
```

### Step 7: Renderer Setup

```jsx
// Create renderer
const renderer = new THREE.WebGLRenderer({
    canvas: canvas,
    antialias: true
});
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
```

### Step 8: Animation Loop

```jsx
// Animation loop - rotate spheres and animate lights
function animate() {
    requestAnimationFrame(animate);

    const time = Date.now() * 0.001; // Convert to seconds

    // Rotate all spheres on Y axis
    spheres.forEach(sphere => {
        sphere.rotation.y = time * 0.5;
        sphere.rotation.x = Math.sin(time * 0.3) * 0.2;
    });

    // Animate point lights in circles
    pointLight.position.x = Math.sin(time) * 8;
    pointLight.position.z = Math.cos(time) * 8;

    pointLight2.position.x = Math.sin(time + Math.PI) * 8;
    pointLight2.position.z = Math.cos(time + Math.PI) * 8;

    renderer.render(scene, camera);
}

animate();

// Handle window resize
window.addEventListener('resize', () => {
    camera.aspect = window.innerWidth / window.innerHeight;
    camera.updateProjectionMatrix();
    renderer.setSize(window.innerWidth, window.innerHeight);
});
```

## Complete Code

Here's everything together:

```jsx
import * as THREE from "three";

const canvas = document.getElementById("canvas");

// Scene setup
const scene = new THREE.Scene();
scene.background = new THREE.Color(0x1a1a2e);

// Camera setup
const camera = new THREE.PerspectiveCamera(
    75,
    window.innerWidth / window.innerHeight,
    0.1,
    1000
);
camera.position.set(0, 0, 15);
camera.lookAt(0, 0, 0);

// Lighting
const ambientLight = new THREE.AmbientLight(0xffffff, 0.3);
scene.add(ambientLight);

const directionalLight = new THREE.DirectionalLight(0xffffff, 1);
directionalLight.position.set(5, 5, 5);
scene.add(directionalLight);

const pointLight = new THREE.PointLight(0xff00ff, 1);
pointLight.position.set(-5, 3, 5);
scene.add(pointLight);

const pointLight2 = new THREE.PointLight(0x00ffff, 0.8);
pointLight2.position.set(5, -3, 5);
scene.add(pointLight2);

// Materials
const basicMaterial = new THREE.MeshBasicMaterial({
    color: 0xff6b6b,
    wireframe: false
});

const lambertMaterial = new THREE.MeshLambertMaterial({
    color: 0x4ecdc4,
    emissive: 0x000000,
    wireframe: false
});

const phongMaterial = new THREE.MeshPhongMaterial({
    color: 0xffe66d,
    shininess: 100,
    specular: 0xffffff,
    emissive: 0x000000,
    wireframe: false
});

const standardMaterial = new THREE.MeshStandardMaterial({
    color: 0xa8e6cf,
    metalness: 0.5,
    roughness: 0.5,
    emissive: 0x000000,
    wireframe: false
});

const physicalMaterial = new THREE.MeshPhysicalMaterial({
    color: 0xffa8e8,
    metalness: 0.8,
    roughness: 0.2,
    clearcoat: 1.0,
    clearcoatRoughness: 0.1,
    reflectivity: 1.0,
    emissive: 0x000000,
    wireframe: false
});

// Create spheres
const sphereGeometry = new THREE.SphereGeometry(1.5, 64, 64);

const basicSphere = new THREE.Mesh(sphereGeometry, basicMaterial);
basicSphere.position.set(-8, 2, 0);
scene.add(basicSphere);

const lambertSphere = new THREE.Mesh(sphereGeometry, lambertMaterial);
lambertSphere.position.set(-4, 2, 0);
scene.add(lambertSphere);

const phongSphere = new THREE.Mesh(sphereGeometry, phongMaterial);
phongSphere.position.set(0, 2, 0);
scene.add(phongSphere);

const standardSphere = new THREE.Mesh(sphereGeometry, standardMaterial);
standardSphere.position.set(4, 2, 0);
scene.add(standardSphere);

const physicalSphere = new THREE.Mesh(sphereGeometry, physicalMaterial);
physicalSphere.position.set(8, 2, 0);
scene.add(physicalSphere);

const spheres = [basicSphere, lambertSphere, phongSphere, standardSphere, physicalSphere];
const materials = [basicMaterial, lambertMaterial, phongMaterial, standardMaterial, physicalMaterial];

// Create labels
function createTextLabel(text) {
    const canvas = document.createElement('canvas');
    const context = canvas.getContext('2d');
    canvas.width = 512;
    canvas.height = 128;

    context.fillStyle = '#000000';
    context.fillRect(0, 0, canvas.width, canvas.height);

    context.font = 'Bold 48px Arial';
    context.fillStyle = '#ffffff';
    context.textAlign = 'center';
    context.fillText(text, canvas.width / 2, canvas.height / 2 + 16);

    const texture = new THREE.CanvasTexture(canvas);
    const spriteMaterial = new THREE.SpriteMaterial({ map: texture });
    const sprite = new THREE.Sprite(spriteMaterial);
    sprite.scale.set(4, 1, 1);

    return sprite;
}

const labels = [
    { text: 'Basic', position: [-8, 0, 0] },
    { text: 'Lambert', position: [-4, 0, 0] },
    { text: 'Phong', position: [0, 0, 0] },
    { text: 'Standard', position: [4, 0, 0] },
    { text: 'Physical', position: [8, 0, 0] }
];

labels.forEach(label => {
    const sprite = createTextLabel(label.text);
    sprite.position.set(label.position[0], label.position[1], label.position[2]);
    scene.add(sprite);
});

// Interactive controls
const metalnessSlider = document.getElementById('metalness');
const roughnessSlider = document.getElementById('roughness');
const opacitySlider = document.getElementById('opacity');
const wireframeBtn = document.getElementById('toggleWireframe');
const emissiveBtn = document.getElementById('toggleEmissive');

const metalnessValue = document.getElementById('metalnessValue');
const roughnessValue = document.getElementById('roughnessValue');
const opacityValue = document.getElementById('opacityValue');

metalnessSlider.addEventListener('input', (e) => {
    const value = parseFloat(e.target.value);
    metalnessValue.textContent = value.toFixed(1);
    standardMaterial.metalness = value;
    physicalMaterial.metalness = value;
});

roughnessSlider.addEventListener('input', (e) => {
    const value = parseFloat(e.target.value);
    roughnessValue.textContent = value.toFixed(1);
    standardMaterial.roughness = value;
    physicalMaterial.roughness = value;
});

opacitySlider.addEventListener('input', (e) => {
    const value = parseFloat(e.target.value);
    opacityValue.textContent = value.toFixed(1);
    materials.forEach(material => {
        material.transparent = value < 1.0;
        material.opacity = value;
    });
});

let wireframeEnabled = false;
wireframeBtn.addEventListener('click', () => {
    wireframeEnabled = !wireframeEnabled;
    materials.forEach(material => {
        material.wireframe = wireframeEnabled;
    });
});

let emissiveEnabled = false;
emissiveBtn.addEventListener('click', () => {
    emissiveEnabled = !emissiveEnabled;
    lambertMaterial.emissive.setHex(emissiveEnabled ? 0x002222 : 0x000000);
    phongMaterial.emissive.setHex(emissiveEnabled ? 0x222200 : 0x000000);
    standardMaterial.emissive.setHex(emissiveEnabled ? 0x002200 : 0x000000);
    physicalMaterial.emissive.setHex(emissiveEnabled ? 0x220022 : 0x000000);
});

// Renderer
const renderer = new THREE.WebGLRenderer({
    canvas: canvas,
    antialias: true
});
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);

// Animation loop
function animate() {
    requestAnimationFrame(animate);

    const time = Date.now() * 0.001;

    spheres.forEach(sphere => {
        sphere.rotation.y = time * 0.5;
        sphere.rotation.x = Math.sin(time * 0.3) * 0.2;
    });

    pointLight.position.x = Math.sin(time) * 8;
    pointLight.position.z = Math.cos(time) * 8;

    pointLight2.position.x = Math.sin(time + Math.PI) * 8;
    pointLight2.position.z = Math.cos(time + Math.PI) * 8;

    renderer.render(scene, camera);
}

animate();

window.addEventListener('resize', () => {
    camera.aspect = window.innerWidth / window.innerHeight;
    camera.updateProjectionMatrix();
    renderer.setSize(window.innerWidth, window.innerHeight);
});
```

## When to Use Each Material

### MeshBasicMaterial

**Performance:** ⚡⚡⚡⚡⚡ (Fastest)

**When to use:**

* UI elements that should always be visible
    
* Backgrounds or sky boxes
    
* Debug visualizations
    
* Objects that shouldn't react to lights
    
* Performance-critical applications with many objects
    

**Pros:** No lighting calculations, always renders fast

**Cons:** Looks flat, no depth or realism

### MeshLambertMaterial

**Performance:** ⚡⚡⚡⚡ (Fast)

**When to use:**

* Matte, non-shiny surfaces
    
* Paper, cardboard, unfinished wood
    
* Fabric, cloth materials
    
* Chalk, matte paint
    
* Mobile applications (good performance)
    

**Pros:** Good performance, realistic for matte surfaces

**Cons:** No specular highlights (can't make shiny things)

### MeshPhongMaterial

**Performance:** ⚡⚡⚡ (Medium)

**When to use:**

* Shiny plastics
    
* Glossy paint
    
* Polished wood
    
* Ceramic surfaces
    
* When you need visible light reflections
    

**Pros:** Good specular highlights, proven technique

**Cons:** Not physically accurate, being replaced by Standard material

### MeshStandardMaterial

**Performance:** ⚡⚡ (Slower)

**When to use:**

* Modern, realistic rendering
    
* Metals (using metalness)
    
* Most general-purpose 3D objects
    
* When you want physically accurate materials
    
* Production-quality projects
    

**Pros:** Physically accurate (PBR), versatile, industry standard

**Cons:** More expensive than Basic/Lambert/Phong

### MeshPhysicalMaterial

**Performance:** ⚡ (Slowest)

**When to use:**

* Car paint with clear coat
    
* Gems and jewellery
    
* Glass and transparent materials
    
* High-end product visualization
    
* When you need the absolute best quality
    

**Pros:** Most realistic, has advanced features (clear coat, transmission)

**Cons:** Most expensive performance-wise, overkill for many use cases

## Performance Comparison

From fastest to slowest:

1. **MeshBasicMaterial** - No lighting calculations
    
2. **MeshLambertMaterial** - Simple lighting (diffuse only)
    
3. **MeshPhongMaterial** - Lighting + specular highlights
    
4. **MeshStandardMaterial** - Full PBR calculations
    
5. **MeshPhysicalMaterial** - PBR + advanced effects
    

**Pro tip:** Use the cheapest material that gives you the look you need! Don't use Physical material if Standard looks good enough.

## What I Learned

### 1\. **Materials Define Appearance**

While geometry defines shape, materials define how that shape looks - the colour, shininess, transparency, and how it reacts to light. They're equally important!

### 2\. **Lighting Dependencies Matter**

* **Basic material** = No lights needed (always visible)
    
* **Lambert, Phong, Standard, Physical** = Need lights to be visible This is crucial when debugging - if your objects are black, check your lights!
    

### 3\. **PBR is the Modern Standard**

The metalness/roughness workflow (Standard and Physical materials) is what modern games and applications use. It's more intuitive than the old specular workflow.

### 4\. **Metalness vs Roughness**

Understanding these two properties unlocks realistic materials:

* **Metalness:** Is it metal or not? (Usually 0 or 1, rarely in between)
    
* **Roughness:** How polished is the surface? (0 = mirror, 1 = matte)
    

### 5\. **Performance vs Quality Trade-off**

You can have 1000 objects with Basic material or 100 with Physical material. Choose based on your needs:

* Mobile/many objects = Basic or Lambert
    
* Desktop/quality matters = Standard or Physical
    

### 6\. **Emissive Adds Depth**

Adding a subtle emissive glow (same colour as the object, very low intensity) can make materials look more alive without affecting performance much.

### 7\. **Transparency Requires Planning**

When using opacity, you must set `transparent: true` first. Also, transparent objects can have sorting issues when overlapping - Three.js renders them back-to-front.

### 8\. **Wireframe Mode is Essential for Learning**

Toggle wireframe mode to see the actual geometry structure. It's incredibly useful for understanding how shapes are built and debugging performance issues.

## Common Material Patterns

Here are some material configurations I've found useful:

### Polished Metal

```jsx
new THREE.MeshStandardMaterial({
    color: 0xaaaaaa,
    metalness: 1.0,
    roughness: 0.2
});
```

### Matte Plastic

```jsx
new THREE.MeshStandardMaterial({
    color: 0x2194ce,
    metalness: 0.0,
    roughness: 0.8
});
```

### Glass

```jsx
new THREE.MeshPhysicalMaterial({
    color: 0xffffff,
    metalness: 0.0,
    roughness: 0.0,
    transmission: 1.0,
    transparent: true
});
```

### Glowing Object

```jsx
new THREE.MeshStandardMaterial({
    color: 0x00ff00,
    emissive: 0x00ff00,
    emissiveIntensity: 0.5
});
```

## **Key takeaways:**

* Basic = fast but flat
    
* Lambert = matte surfaces
    
* Phong = shiny surfaces
    
* Standard = modern PBR (use this most)
    
* Physical = premium quality when you need it
    

The interactive controls in this demo really helped me understand how metalness, roughness, and other properties affect the final look. I recommend building this yourself and playing with the sliders!

Try experimenting with:

* Different metalness/roughness combinations
    
* Adding emissive glows in different colors
    
* Creating transparent materials
    
* Mixing materials on different objects
    
* Adjusting lighting to see how materials react
    

Understanding materials is essential for creating visually appealing 3D experiences. Now I can make things look like metal, plastic, glass, or anything in between!

Happy rendering. 🎨
