Clear Coat and Sheen

PBRMaterial's Clear Coat, Sheen and Anisotropy

Some surfaces need a second lobe. PBRMaterial groups these as sub-configurations, each off until you set isEnabled, so unused ones cost no shader code. In the sweep's third row, clearCoat lays sharp studio reflections over rough paint (car paint, a laminated cover), sheen gives the middle sphere cloth's wide, grazing-angle glow (meant for rough dielectrics), and anisotropy stretches highlights along the surface tangent, smearing the right sphere's softboxes into brushed-metal streaks:

Enabling and tuning the three extra layersJavaScript
paint.clearCoat.isEnabled = true;
paint.clearCoat.roughness = 0.05;                 // default 0: a mirror-smooth coat
cloth.sheen.isEnabled = true;
cloth.sheen.color = new BABYLON.Color3(0.9, 0.85, 1);   // tint the rim
brushed.anisotropy.isEnabled = true;
brushed.anisotropy.direction = new BABYLON.Vector2(0, 1);   // along the tangent's v

The glTF loader maps KHR_materials_clearcoat, _sheen and _anisotropy onto them. Anisotropy needs a sound tangent space: tessellated geometry with UVs, or exported tangents. subSurface (refraction, translucency, scattering) and iridescence work the same way; each layer adds uniforms and texture fetches.

PBRMaterial's second lobes: clear coat over rough paint, cloth sheen and brushed-metal anisotropyHTMLLive
<!doctype html>
<style>
  body { margin: 0; background: #f7f3ec; }
  canvas { width: 100%; max-width: 600px; height: 340px; display: block; touch-action: none; }
</style>
<canvas id="view"></canvas>
<script src="https://cdn.jsdelivr.net/npm/babylonjs@9.28.0/babylon.js"></script>
<script src="https://cdn.jsdelivr.net/npm/babylonjs-gui@9.28.0/babylon.gui.min.js"></script>
<script>
const canvas = document.getElementById('view');
const engine = new BABYLON.Engine(canvas, true);
const scene = new BABYLON.Scene(engine);
scene.clearColor = BABYLON.Color4.FromHexString('#e9e4dcff');
const camera = new BABYLON.ArcRotateCamera('cam', -Math.PI / 2, 1.35, 6, new BABYLON.Vector3(0, 0.1, 0), scene);
camera.attachControl(canvas, true);
const ui = BABYLON.GUI.AdvancedDynamicTexture.CreateFullscreenUI('ui');
new BABYLON.DirectionalLight('key', new BABYLON.Vector3(0.5, -0.7, 0.6), scene).intensity = 1.6;

// A procedural studio for image-based lighting: a dim dome with three bright softboxes,
// rendered once into a ReflectionProbe's cube texture. (The sandboxed preview cannot download
// an .env or .hdr file, so the environment is built in code.)
function createStudioEnvironment(scene) {
  const probe = new BABYLON.ReflectionProbe('studio', 256, scene);
  const dome = BABYLON.MeshBuilder.CreateSphere('dome', { diameter: 60, sideOrientation: BABYLON.Mesh.BACKSIDE }, scene);
  dome.material = new BABYLON.StandardMaterial('domeMat', scene);
  dome.material.emissiveColor = BABYLON.Color3.FromHexString('#6a6158');
  dome.material.disableLighting = true;
  const studio = [dome];
  [[-7, 5, -7], [8, 3, -5], [0, 9, 5]].forEach(([x, y, z], i) => {
    const box = BABYLON.MeshBuilder.CreatePlane('softbox' + i, { width: 9, height: 4 }, scene);
    box.position.set(x, y, z);
    box.lookAt(BABYLON.Vector3.Zero());
    box.rotation.y += Math.PI;                        // turn the lit face toward the center
    box.material = new BABYLON.StandardMaterial('panel' + i, scene);
    box.material.emissiveColor = new BABYLON.Color3(3, 3, 3);   // brighter than white
    box.material.disableLighting = true;
    studio.push(box);
  });
  // The studio is visible only while the probe renders, so the page keeps its light background
  studio.forEach((mesh) => { mesh.isVisible = false; probe.renderList.push(mesh); });
  probe.cubeTexture.onBeforeRenderObservable.add(() => studio.forEach((m) => { m.isVisible = true; }));
  probe.cubeTexture.onAfterRenderObservable.add(() => studio.forEach((m) => { m.isVisible = false; }));
  probe.refreshRate = BABYLON.RenderTargetTexture.REFRESHRATE_RENDER_ONCE;
  scene.environmentTexture = probe.cubeTexture;
  return probe;
}
createStudioEnvironment(scene);

const makePBR = (name, hex, roughness, metallic) => {
  const m = new BABYLON.PBRMaterial(name, scene);
  m.albedoColor = BABYLON.Color3.FromHexString(hex);
  Object.assign(m, { roughness, metallic });
  return m;
};
// Rough red paint with a mirror-smooth clear coat on top, like a laminated cover
const paint = makePBR('paint', '#b5452f', 0.6, 0);
paint.clearCoat.isEnabled = true;
paint.clearCoat.roughness = 0.05;
// Rough purple cloth with a pale sheen at grazing angles
const cloth = makePBR('cloth', '#5b3f99', 0.9, 0);
cloth.sheen.isEnabled = true;
cloth.sheen.color = new BABYLON.Color3(0.9, 0.85, 1);
cloth.sheen.intensity = 1;
// Brushed metal: highlights stretched along the tangent
const brushed = makePBR('brushed', '#c9c3b8', 0.35, 1);
brushed.anisotropy.isEnabled = true;
brushed.anisotropy.intensity = 1;
brushed.anisotropy.direction = new BABYLON.Vector2(0, 1);   // along the tangent's v

[[paint, 'clearCoat'], [cloth, 'sheen'], [brushed, 'anisotropy']].forEach(([material, text], i) => {
  // tessellated spheres have the UVs anisotropy needs for its tangent space
  const ball = BABYLON.MeshBuilder.CreateSphere(text, { diameter: 1.5, segments: 48 }, scene);
  ball.position.x = (i - 1) * 1.8;
  ball.material = material;
  const label = new BABYLON.GUI.TextBlock('', `${material.name}.${text}`);
  Object.assign(label, { color: '#333', fontSize: 13, resizeToFit: true });
  ui.addControl(label);
  label.linkWithMesh(ball);
  label.linkOffsetY = 80;
});
scene.onBeforeRenderObservable.add(() => {
  scene.meshes.forEach((m) => { if (m.material instanceof BABYLON.PBRMaterial) m.rotation.y += 0.004; });
});
engine.runRenderLoop(() => scene.render());
addEventListener('resize', () => engine.resize());
</script>