Three.js阴影无法显示问题求助
Three.js 3D模型阴影不显示的修复方案
针对你代码中阴影无法显示的问题,以下是几个关键修复步骤:
1. 配置平行光的阴影相机参数
平行光(DirectionalLight)默认阴影相机的视锥体范围极小,无法覆盖你的模型,导致阴影无法被渲染。需要手动调整参数:
const topLight = new THREE.DirectionalLight(0xffffff, 3); topLight.position.set(-1.05307, 2, 4.52525 ); // 适当提高Y轴位置,让阴影向下投射 topLight.castShadow = true; // 设置阴影相机视锥体范围 topLight.shadow.camera.left = -5; topLight.shadow.camera.right = 5; topLight.shadow.camera.top = 5; topLight.shadow.camera.bottom = -5; topLight.shadow.camera.near = 0.1; topLight.shadow.camera.far = 15; topLight.shadow.camera.updateProjectionMatrix(); // 提高阴影贴图分辨率(可选,让阴影更清晰) topLight.shadow.mapSize.set(2048, 2048); scene.add(topLight);
2. 递归设置模型子网格的阴影属性
直接给GLTF场景对象设置castShadow和receiveShadow不会自动应用到所有子网格,必须遍历模型的所有Mesh单独配置:
loader.load( `models/${objToRender}/scene.gltf`, function (gltf) { object = gltf.scene; // 遍历所有子网格设置阴影属性 object.traverse(child => { if (child.isMesh) { child.castShadow = true; child.receiveShadow = true; } }); scene.add(object); animate(); }, // 进度、错误回调保持不变 );
3. 添加地面接收阴影
如果场景中没有可接收阴影的物体,模型的阴影会没有投射目标,需要添加一个平面作为地面:
// 在场景初始化后添加地面 const ground = new THREE.Mesh( new THREE.PlaneGeometry(10, 10), new THREE.MeshStandardMaterial({ color: 0xcccccc }) ); ground.rotation.x = -Math.PI / 2; // 旋转为水平方向 ground.position.y = -1.5; // 调整到恐龙模型下方 ground.receiveShadow = true; scene.add(ground);
4. 降低环境光强度(可选)
你设置的环境光强度为1,会让阴影变得非常不明显,适当降低可以突出阴影效果:
const ambientLight = new THREE.AmbientLight(0xFFFFFF, objToRender === "dino" ? 0.3 : 1); scene.add(ambientLight);
修改后的完整代码
import * as THREE from "https://cdn.skypack.dev/three@0.129.0/build/three.module.js"; import { OrbitControls } from "https://cdn.skypack.dev/three@0.129.0/examples/jsm/controls/OrbitControls.js"; import { GLTFLoader } from "https://cdn.skypack.dev/three@0.129.0/examples/jsm/loaders/GLTFLoader.js"; const scene = new THREE.Scene(); const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000); let mouseX = window.innerWidth / 2; let mouseY = window.innerHeight / 2; let object; let controls; let objToRender = 'dino'; const renderer = new THREE.WebGLRenderer({ alpha: true }); renderer.setSize(window.innerWidth, window.innerHeight); renderer.shadowMap.enabled = true; renderer.shadowMap.type = THREE.PCFSoftShadowMap; document.getElementById("container3D").appendChild(renderer.domElement); camera.position.z = objToRender === "dino" ? 4 : 500; // 添加地面接收阴影 const ground = new THREE.Mesh( new THREE.PlaneGeometry(10, 10), new THREE.MeshStandardMaterial({ color: 0xcccccc }) ); ground.rotation.x = -Math.PI / 2; ground.position.y = -1.5; ground.receiveShadow = true; scene.add(ground); // 配置平行光及阴影 const topLight = new THREE.DirectionalLight(0xffffff, 3); topLight.position.set(-1.05307, 2, 4.52525 ); topLight.castShadow = true; // 设置阴影相机参数 topLight.shadow.camera.left = -5; topLight.shadow.camera.right = 5; topLight.shadow.camera.top = 5; topLight.shadow.camera.bottom = -5; topLight.shadow.camera.near = 0.1; topLight.shadow.camera.far = 15; topLight.shadow.camera.updateProjectionMatrix(); // 提高阴影贴图分辨率 topLight.shadow.mapSize.set(2048, 2048); scene.add(topLight); // 降低环境光强度,让阴影更明显 const ambientLight = new THREE.AmbientLight(0xFFFFFF, objToRender === "dino" ? 0.3 : 1); scene.add(ambientLight); const loader = new GLTFLoader(); loader.load( `models/${objToRender}/scene.gltf`, function (gltf) { object = gltf.scene; // 遍历子网格设置阴影属性 object.traverse(child => { if (child.isMesh) { child.castShadow = true; child.receiveShadow = true; } }); scene.add(object); animate(); }, function (xhr) { const percentage = (xhr.loaded / xhr.total * 100).toFixed(2); console.log(percentage + '% loaded'); document.getElementById("loadingProgress").innerText = `Loading: ${percentage}%`; }, function (error) { console.error(error); } ); if (objToRender === "dino") { controls = new OrbitControls(camera, renderer.domElement); } function animate() { requestAnimationFrame(animate); if (object && objToRender === "eye") { object.rotation.y = -3 + mouseX / window.innerWidth * 3; object.rotation.x = -1.2 + mouseY * 2.5 / window.innerHeight; } renderer.render(scene, camera); } window.addEventListener("resize", function () { camera.aspect = window.innerWidth / window.innerHeight; camera.updateProjectionMatrix(); renderer.setSize(window.innerWidth, window.innerHeight); }); document.onmousemove = (e) => { mouseX = e.clientX; mouseY = e.clientY; };
内容的提问来源于stack exchange,提问作者Aleksandar Despotovic
相关产品推荐
相关产品推荐

