Soft Shadows, Blur and Bias

One property picks the filter that softens Framebuffers and Shadows's hard comparison: usePercentageCloserFiltering (PCF, PCF Soft Shadows), usePoissonSampling, useBlurExponentialShadowMap and useBlurCloseExponentialShadowMap (exponential maps blurred like images), or useContactHardeningShadow (PCSS, whose penumbra widens away from the caster); filteringQuality trades taps for speed. A small 256-pixel map shows the differences:

One book, four shadow filters: hard, PCF, blurred CESM and PCSSHTMLLive
<div id="row" style="display: flex; gap: 4px; font: 13px sans-serif; text-align: center"></div>
<script src="https://cdn.jsdelivr.net/npm/babylonjs@9.28.0/babylon.js"></script>
<script>
{
  const filters = {
    hard: () => {},
    PCF: (sg) => { sg.usePercentageCloserFiltering = true; },
    'blurred CESM': (sg) => { sg.useBlurCloseExponentialShadowMap = true; },
    PCSS: (sg) => { sg.useContactHardeningShadow = true; },
  };
  let pending = Object.keys(filters).length;
  for (const [label, useFilter] of Object.entries(filters)) {
    const panel = document.createElement('div');
    panel.innerHTML = `<canvas style="width: 152px; height: 115px"></canvas><br>${label}`;
    row.append(panel);
    const engine = new BABYLON.Engine(panel.firstChild, true, {}, true);
    const scene = new BABYLON.Scene(engine);
    scene.clearColor = BABYLON.Color4.FromHexString('#eceff1ff');
    const target = new BABYLON.Vector3(-0.2, 0.2, 0);
    new BABYLON.ArcRotateCamera('cam', -2.1, 0.95, 1.9, target, scene);
    new BABYLON.HemisphericLight('fill', new BABYLON.Vector3(0, 1, 0), scene).intensity = 0.4;
    const sun = new BABYLON.DirectionalLight('sun', new BABYLON.Vector3(-0.6, -1, 0.5), scene);
    Object.assign(sun, { shadowMinZ: -2, shadowMaxZ: 3 });      // tight depth range
    const plank = BABYLON.MeshBuilder.CreateGround('oak', { width: 2.4, height: 1.6 }, scene);
    plank.material = new BABYLON.StandardMaterial('oak', scene);
    plank.material.diffuseColor = BABYLON.Color3.FromHexString('#b08d6a');
    plank.receiveShadows = true;
    const size = { width: 0.12, height: 0.75, depth: 0.5 };
    const book = BABYLON.MeshBuilder.CreateBox('book', size, scene);
    book.position.y = 0.375;
    book.rotation.z = 0.35;                                  // leaning, touching the plank
    const shadows = new BABYLON.ShadowGenerator(256, sun);
    shadows.addShadowCaster(book);
    useFilter(shadows);
    scene.executeWhenReady(() => engine.runRenderLoop(() => scene.render()));
    scene.onAfterRenderObservable.add(() => {
      if (scene.getFrameId() === 5 && --pending === 0) window.__done = true;
    });
  }
}
</script>
Browser output of Listing 7.40
Browser output of 40

The hard edge shows the map's texels as stair steps; PCF smooths them; the blurred close exponential map blurs every edge alike; PCSS stays sharp where the book touches the plank and spreads away from it. Every filter meets shadow acne (Acne and Bias): bias (default 0.00005) offsets the stored depth, normalBias shrinks casters along their normals, fixing sloped surfaces without a large bias's peter-panning, and forceBackFacesOnly renders back faces, a cheap fix for closed meshes.