Three.js 阴影不显示?关键一步你可能写错了

three.js 中阴影无法显示的常见原因包括光源、物体和接收面未正确配置;本文直击你代码中的关键拼写错误,并系统梳理阴影启用的完整必要条件。

在你的代码中,阴影未显示的直接原因是一个易被忽略的拼写错误:你为地面平面(planeObj)设置了 plane.receiveShadow = true,但变量名实际是 planeObj,因此这行赋值根本未生效——plane 是几何体(THREE.PlaneGeometry 实例),它没有 receiveShadow 属性;而真正需要接收阴影的是网格对象 planeObj(THREE.Mesh 实例)。

✅ 正确写法应为:

planeObj.receiveShadow = true; // ✅ 作用于 Mesh 实例

但这只是“冰山一角”。要让 Three.js 阴影正常工作,必须同时满足以下全部条件

? 四大必备条件(缺一不可)

  1. 渲染器启用阴影映射

    renderer.shadowMap.enabled = true;
    renderer.shadowMap.type = THREE.PCFSoftShadowMap; // 推荐软阴影
  2. 光源启用阴影投射且配置合理尺寸

    light.castShadow = true;
    light.shadow.mapSize.width = 1024; // 建议 ≥ 512,越大越清晰(但性能开销增加)
    light.shadow.mapSize.height = 1024;
    // ⚠️ 补充:点光源阴影需注意视锥范围(可选)
    light.shadow.camera.near = 0.1;
    light.shadow.camera.far = 10;
    light.shadow.camera.fov = 90;
  3. 投射阴影的物体显式开启 castS

    hadow

    sphere.castShadow = true; // ✅ 已正确设置
  4. 接收阴影的物体显式开启 receiveShadow

    planeObj.receiveShadow = true; // ✅ 必须作用于 Mesh 对象,非 Geometry!

? 额外优化建议

  • 使用 MeshStandardMaterial 或 MeshPhysicalMaterial(你已正确选用)——基础 MeshBasicMaterial 和 MeshLambertMaterial 不支持阴影
  • 确保接收面(如地面)有足够几何细分(当前 PlaneGeometry(20,20,1,1) 足够),但若使用自定义着色器,需手动处理 shadow map 采样。
  • 若阴影仍模糊或缺失,检查光源位置是否被遮挡,或调用 light.shadow.camera.updateProjectionMatrix() 强制更新(尤其动态移动光源时)。

✅ 完整修正片段(关键行已高亮)

// ... 其他初始化保持不变

// add plane object: ground
const plane = new THREE.PlaneGeometry(20, 20);
const planeMaterial = new THREE.MeshStandardMaterial({ color: 0xeeeeee });
const planeObj = new THREE.Mesh(plane, planeMaterial);
planeObj.position.y = -1;
planeObj.rotation.x = -Math.PI / 2;
planeObj.receiveShadow = true; // ✅ 修正:作用于 planeObj,非 plane
scene.add(planeObj);
? 提示:可通过 renderer.debug = { showShadowMap: true }(Three.js r160+)临时可视化阴影贴图,快速定位映射问题。

只要严格满足上述四点,阴影将立即呈现。你遇到的问题看似微小,却恰恰暴露了 Three.js 渲染管线中「对象层级」的关键逻辑——阴影属性永远属于 Mesh,而非其几何体或材质。