StandardMaterial sums four colored channels, each optionally multiplied by a texture: diffuse (the base color, lit by each light's Lambert term), specular (the Blinn-Phong highlight of Specular Lighting and Fog, sharpened by specularPower, default 64), emissive (added unlit, so it glows in the dark) and ambient (multiplied by scene.ambientColor, black by default). The defaults are white diffuse and white specular, which is why an untouched material looks like plastic, and why BookNest's paint() sets specular to black for matte paint:
const blue = BABYLON.Color3.FromHexString('#1f5f8b');
const matte = new BABYLON.StandardMaterial('matte', scene);
matte.diffuseColor = blue;
matte.specularColor = BABYLON.Color3.Black(); // no highlight
const glossy = matte.clone('glossy');
glossy.specularColor = new BABYLON.Color3(0.6, 0.6, 0.6);
glossy.specularPower = 128; // small, sharp highlight
const sign = new BABYLON.StandardMaterial('sign', scene);
sign.emissiveColor = blue; // visible with no light at all
sign.disableLighting = true; // and skip the lighting codeEach channel has a texture slot (diffuseTexture, specularTexture, emissiveTexture, ambientTexture), plus bumpTexture for normal maps (Normal Maps), opacityTexture, reflectionTexture and lightmapTexture. maxSimultaneousLights (default 4) caps how many lights reach a mesh, since each one lengthens the shader.
<!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('#ebe5dbff');
scene.ambientColor = new BABYLON.Color3(0.3, 0.3, 0.3); // black by default
const camera = new BABYLON.ArcRotateCamera('cam', -Math.PI / 2, 1.3, 6.8, new BABYLON.Vector3(0, 0.2, 0), scene);
camera.attachControl(canvas, true);
// One point light circling in front makes the specular highlights move
const lamp = new BABYLON.PointLight('lamp', new BABYLON.Vector3(0, 2, -3), scene);
const fill = new BABYLON.HemisphericLight('fill', new BABYLON.Vector3(0, 1, 0), scene);
fill.intensity = 0.35;
fill.specular = BABYLON.Color3.Black(); // a soft fill with no highlight of its own
const ui = BABYLON.GUI.AdvancedDynamicTexture.CreateFullscreenUI('ui');
const blue = BABYLON.Color3.FromHexString('#1f5f8b');
const matte = new BABYLON.StandardMaterial('matte', scene);
matte.diffuseColor = blue;
matte.specularColor = BABYLON.Color3.Black(); // no highlight
const glossy = matte.clone('glossy');
glossy.specularColor = new BABYLON.Color3(0.6, 0.6, 0.6);
glossy.specularPower = 128; // small, sharp highlight
const plastic = new BABYLON.StandardMaterial('default', scene); // white specular: plastic look
plastic.diffuseColor = blue;
const sign = new BABYLON.StandardMaterial('sign', scene);
sign.emissiveColor = blue; // visible with no light at all
sign.disableLighting = true; // and skip the lighting code
const ambient = matte.clone('ambient');
ambient.ambientColor = new BABYLON.Color3(1, 0.8, 0.4); // multiplied by scene.ambientColor
const materials = [[matte, 'diffuse only\n(specular black)'], [plastic, 'defaults\n(white specular)'],
[glossy, 'specular 0.6\npower 128'], [sign, 'emissive\nunlit'],
[ambient, 'diffuse +\nambient tint']];
materials.forEach(([material, text], i) => {
const ball = BABYLON.MeshBuilder.CreateSphere(material.name, { diameter: 1.05, segments: 32 }, scene);
ball.position.x = (i - 2) * 1.6;
ball.material = material;
const label = new BABYLON.GUI.TextBlock('', text);
Object.assign(label, { color: '#333', fontSize: 12, resizeToFit: true });
ui.addControl(label);
label.linkWithMesh(ball);
label.linkOffsetY = 62;
});
let t = 0;
scene.onBeforeRenderObservable.add(() => {
t += engine.getDeltaTime() / 1000;
lamp.position.set(Math.sin(t * 0.8) * 4, 2, -3);
});
engine.runRenderLoop(() => scene.render());
addEventListener('resize', () => engine.resize());
</script>