MeshPhysicalMaterial

MeshPhysicalMaterial for Clearcoat and Transmission

MeshPhysicalMaterial adds layers, most matching a glTF extension: clearcoat (a varnish, as on a laminated cover), transmission with thickness and ior (refracting glass), sheen (cloth), iridescence (soap film), anisotropy, dispersion and, new in r186, retroreflectivity. A shelf stands behind so the glass bends it:

Standard, clearcoat, transmission, sheen and iridescenceHTML
<script src="three-map.js"></script>
<script type="module">
import * as THREE from 'three';
import * as BN from './bookshelf.js';
const stage = BN.createStage({ width: 640, height: 130 });
BN.addEnvironment(stage.renderer, stage.scene, 0.4);
const shelf = BN.createShelf();
shelf.position.set(0, -0.45, -2.5);
stage.scene.add(shelf);
const cover = { color: '#b5452f', roughness: 0.6 };
BN.materialRow(stage, [
  new THREE.MeshStandardMaterial(cover),
  new THREE.MeshPhysicalMaterial({ ...cover, clearcoat: 1, clearcoatRoughness: 0.05 }),
  new THREE.MeshPhysicalMaterial({ roughness: 0, transmission: 1, thickness: 0.6, ior: 1.5 }),
  new THREE.MeshPhysicalMaterial({ color: '#5b3f99', sheen: 1, sheenColor: '#fff' }),
  new THREE.MeshPhysicalMaterial({ color: '#1f5f8b', roughness: 0.2, iridescence: 1 }),
]);
BN.renderFrames(stage.renderer, stage.scene, stage.camera);
</script>
Browser output of Listing 5.12
Browser output of 12

Each layer adds shader cost; transmission most, since the opaque scene is first drawn to a texture the glass samples. Keep MeshStandardMaterial unless a layer shows.

MeshPhysicalMaterial layers: clearcoat, glass transmission, sheen and iridescence in front of a shelfHTMLLive
<!doctype html>
<style>
  body { margin: 0; background: #f5f1e8; font: 12px system-ui, sans-serif; color: #263238; }
  canvas { display: block; max-width: 100%; }
  .labels { display: flex; width: 600px; max-width: 100%; text-align: center; }
  .labels div { flex: 1; }
</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>
<script type="module">
import * as THREE from 'three';
import { RoomEnvironment } from 'three/addons/environments/RoomEnvironment.js';

const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setPixelRatio(Math.min(devicePixelRatio, 2));
renderer.setSize(600, 240);
renderer.toneMapping = THREE.NeutralToneMapping;
document.body.prepend(renderer.domElement);
const scene = new THREE.Scene();
scene.background = new THREE.Color('#fbf8f2');
const camera = new THREE.PerspectiveCamera(30, 600 / 240, 0.1, 50);
camera.position.set(0, 0.2, 5.2);
camera.lookAt(0, 0, 0);
const pmrem = new THREE.PMREMGenerator(renderer);
scene.environment = pmrem.fromScene(new RoomEnvironment(), 0.04).texture;
scene.environmentIntensity = 0.6;
pmrem.dispose();
const sun = new THREE.DirectionalLight('#ffffff', 2);
sun.position.set(2, 3, 4);
scene.add(sun);

// A shelf behind the row, so the glass sphere has something to bend
const shelf = new THREE.Group();
['#1f5f8b', '#5b3f99', '#e09a10', '#3f7d3a', '#b5452f', '#2a9d8f', '#1f5f8b', '#e09a10'].forEach((color, i) => {
  const book = new THREE.Mesh(new THREE.BoxGeometry(0.3, 1.1, 0.5), new THREE.MeshStandardMaterial({ color }));
  book.position.x = (i - 3.5) * 0.34;
  shelf.add(book);
});
shelf.position.set(0, 0, -2.5);
scene.add(shelf);

const cover = { color: '#b5452f', roughness: 0.6 };
const materials = [
  new THREE.MeshStandardMaterial(cover),
  new THREE.MeshPhysicalMaterial({ ...cover, clearcoat: 1, clearcoatRoughness: 0.05 }),     // varnish
  new THREE.MeshPhysicalMaterial({ roughness: 0, transmission: 1, thickness: 0.6, ior: 1.5 }), // glass
  new THREE.MeshPhysicalMaterial({ color: '#5b3f99', sheen: 1, sheenColor: '#ffffff', roughness: 0.8 }), // cloth
  new THREE.MeshPhysicalMaterial({ color: '#1f5f8b', roughness: 0.2, iridescence: 1 }),       // soap film
];
const spheres = materials.map((material, i) => {
  const sphere = new THREE.Mesh(new THREE.SphereGeometry(0.38, 64, 32), material);
  sphere.position.x = (i - 2) * 0.95;
  scene.add(sphere);
  return sphere;
});

const labels = document.body.appendChild(document.createElement('div'));
labels.className = 'labels';
labels.innerHTML = ['Standard', 'clearcoat', 'transmission', 'sheen', 'iridescence']
  .map((t) => `<div>${t}</div>`).join('');

renderer.setAnimationLoop((time) => {
  shelf.position.x = Math.sin(time / 1500) * 0.4;       // slide the shelf behind the glass
  renderer.render(scene, camera);
});
</script>