Three.js 阴影不显示的常见原因与修复指南

1次阅读

Three.js 阴影不显示的常见原因与修复指南

本文详解 Three.js 中阴影无法渲染的三大典型问题:全局阴影开关未启用、receiveShadow 拼写错误、以及语法错误导致脚本中断,并提供可直接运行的修复代码与关键配置要点。

本文详解 three.js 中阴影无法渲染的三大典型问题:全局阴影开关未启用、`receiveshadow` 拼写错误、以及语法错误导致脚本中断,并提供可直接运行的修复代码与关键配置要点。

在 Three.js 中实现真实感阴影看似简单,但常因细微疏漏导致 castShadow 和 receiveShadow 完全失效——表面配置看似完整,实际阴影却完全不可见。这并非引擎缺陷,而是由几个关键前提未满足所致。以下将逐项剖析并给出可落地的解决方案。

✅ 必须满足的三大前提条件

Three.js 的阴影系统是显式启用、逐对象控制、全局依赖的,缺一不可:

  1. 全局启用阴影映射(Shadow map
    即使所有对象都设置了 castShadow = true 和 receiveShadow = true,若未开启渲染器的阴影功能,一切配置均无效:

    renderer.shadowMap.enabled = true; // ✅ 必须放在 renderer 初始化之后、渲染循环之前
  2. 正确拼写 receiveShadow(非 recieveShadow)
    这是最常见的低级错误。JavaScript 不会报错(仅静默忽略),但 ground.recieveShadow = true 实际创建了一个无意义的自定义属性,而真正的 receiveShadow 仍为 false:

    // ❌ 错误拼写(无效) ground.recieveShadow = true;  // ✅ 正确拼写(关键!) ground.receiveShadow = true;
  3. 确保脚本无语法/运行时错误
    原代码末尾存在致命笔误:scene.add(ground)j —— 多余的 j 会导致整个 <script type="module"> 执行中断,后续 renderer.shadowMap.enabled = true 根本不会执行。务必检查控制台报错(如 Uncaught SyntaxError),并清理非法字符。</script>

? 完整修复后代码(已验证)

<style>   body { margin: 0; overflow: hidden; } </style>  <script async src="https://unpkg.com/es-module-shims@1.10.0/dist/es-module-shims.js"></script> <script type="importmap"> {   "imports": {     "three": "https://unpkg.com/three@0.162.0/build/three.module.js",     "three/addons/": "https://unpkg.com/three@0.162.0/examples/jsm/"   } } </script>  <script type="module"> import * as THREE from 'three'; import { OrbitControls } from 'three/addons/controls/OrbitControls.js';  // 场景、相机、渲染器 const scene = new THREE.Scene(); scene.background = new THREE.Color(0xf0f0f0); const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000); camera.position.set(0, 5, 8);  const renderer = new THREE.WebGLRenderer({ antialias: true }); renderer.setSize(window.innerWidth, window.innerHeight); renderer.shadowMap.enabled = true; // ✅ 全局启用阴影(关键位置!) renderer.shadowMap.type = THREE.PCFSoftShadowMap; // 可选:更柔和的阴影 document.body.appendChild(renderer.domElement);  // 控制器 const controls = new OrbitControls(camera, renderer.domElement);  // 立方体(投射阴影) const cubeGeometry = new THREE.BoxGeometry(1, 1, 1); const cubeMaterial = new THREE.MeshStandardMaterial({ color: 0x00ff00 }); const cube = new THREE.Mesh(cubeGeometry, cubeMaterial); cube.castShadow = true; // ✅ 允许投射 cube.receiveShadow = false; // 立方体自身不接收阴影(通常如此) scene.add(cube);  // 地面(接收阴影) const groundGeometry = new THREE.PlaneGeometry(10, 10); const groundMaterial = new THREE.MeshStandardMaterial({    color: 0x0000ff,   side: THREE.DoubleSide  }); const ground = new THREE.Mesh(groundGeometry, groundMaterial); ground.rotation.x = -Math.PI / 2; // 旋转至水平 ground.position.y = -3; ground.receiveShadow = true; // ✅ 正确拼写! scene.add(ground);  // 平行光(投射阴影) const light = new THREE.DirectionalLight(0xffffff, 1); light.castShadow = true; light.position.set(5, 10, 7); // ? 关键:配置阴影贴图参数(否则默认分辨率极低,阴影模糊或消失) light.shadow.mapSize.width = 1024; light.shadow.mapSize.height = 1024; light.shadow.camera.near = 0.5; light.shadow.camera.far = 50; light.shadow.camera.left = -10; light.shadow.camera.right = 10; light.shadow.camera.top = 10; light.shadow.camera.bottom = -10; scene.add(light);  // 环境光(保证基础可见性) scene.add(new THREE.AmbientLight(0x404040, 2));  // 动画循环 function animate() {   requestAnimationFrame(animate);   cube.rotation.x += 0.01;   cube.rotation.y += 0.01;   renderer.render(scene, camera); } animate();  // 响应窗口大小变化 window.addEventListener('resize', () => {   camera.aspect = window.innerWidth / window.innerHeight;   camera.updateProjectionMatrix();   renderer.setSize(window.innerWidth, window.innerHeight); }); </script>

⚠ 注意事项与进阶提示

  • 阴影贴图分辨率:light.shadow.mapSize 默认为 512×512,复杂场景建议设为 1024×1024 或更高,否则阴影边缘严重锯齿或丢失细节。
  • 阴影相机范围:light.shadow.camera.* 参数必须包围所有需要投射/接收阴影的对象。范围过小会导致阴影被裁剪;过大则降低分辨率精度。可临时添加 light.shadow.cameraHelper = new THREE.CameraHelper(light.shadow.camera) 并 scene.add(light.shadow.cameraHelper) 可视化调试。
  • 材质兼容性:MeshBasicMaterial 和 MeshDepthMaterial 不支持阴影。确保使用 MeshStandardMaterial、MeshPhongMaterial 等支持光照计算的材质。
  • 性能权衡:阴影计算开销较大。生产环境可对静态物体预烘焙阴影,或动态物体启用 shadowMap.autoUpdate = false 并手动调用 renderer.shadowMap.needsUpdate = true。

遵循以上三原则并检查拼写与语法,Three.js 阴影即可稳定呈现。记住:shadowMap.enabled 是总开关,castShadow/receiveShadow 是个体权限,而正确的单词拼写与无错误脚本是这一切生效的基石。

text=ZqhQzanResources