ThreeJS中导入的glTF模型无法向PlaneGeometry地面投射阴影
ThreeJS中glTF模型无法向平面地面投射阴影问题
我正在将glTF模型导入ThreeJS项目,使用PlaneGeometry创建平面作为地面,需要实现模型向地面投射阴影的效果。

我已经尝试开启渲染器的阴影映射配置:
renderer.shadowMap.enabled = true;
渲染器初始化代码如下:
const renderer = new THREE.WebGLRenderer({ alpha: true });
同时配置了两盏场景灯光,平行光已开启投射阴影属性:
const hemiLight = new THREE.HemisphereLight(0xffffff, 0x444444); hemiLight.position.set(0, 10, 0); scene.add( hemiLight ); const dirLight = new THREE.DirectionalLight(0xffffff); dirLight.position.set(0, 0, 10); dirLight.castShadow = true; dirLight.shadow.camera.top = 200; dirLight.shadow.camera.bottom = -200; dirLight.shadow.camera.left = - 200; dirLight.shadow.camera.right = 200; dirLight.shadow.camera.near = 0.1; dirLight.shadow.camera.far = 500; scene.add( dirLight );
我已为地面设置receiveShadow属性,遍历glTF模型所有Mesh节点设置castShadow属性,仍无法正常显示阴影,完整代码如下:
body { margin: 0px; padding: 0px; } div#container canvas { cursor: grab; } div#container canvas:active { cursor: grabbing; }
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Import 3D Model into Three JS</title> <script type="module" defer> import * as THREE from 'https://cdn.skypack.dev/three@0.129.0/build/three.module.js'; import { GLTFLoader } from 'https://cdn.skypack.dev/three@0.129.0/examples/jsm/loaders/GLTFLoader.js'; // for .glb and .gltf.glb // import { OBJLoader } from 'https://cdn.skypack.dev/three@0.129.0/examples/jsm/loaders/OBJLoader.js'; // for .obj import { OrbitControls } from 'https://cdn.skypack.dev/three@0.129.0/examples/jsm/controls/OrbitControls.js'; const container = document.querySelector('div#container'); const path_to_model = './ImportModel/Mini-Game Variety Pack/Models/gltf/tree_forest.gltf.glb'; const loader = new GLTFLoader(); const scene = new THREE.Scene(); const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.01, 500); // Add lights const hemiLight = new THREE.HemisphereLight(0xffffff, 0x444444); hemiLight.position.set(0, 10, 0); scene.add( hemiLight ); const dirLight = new THREE.DirectionalLight(0xffffff); dirLight.position.set(0, 0, 10); dirLight.castShadow = true; dirLight.shadow.camera.top = 200; dirLight.shadow.camera.bottom = -200; dirLight.shadow.camera.left = - 200; dirLight.shadow.camera.right = 200; dirLight.shadow.camera.near = 0.1; dirLight.shadow.camera.far = 500; scene.add( dirLight ); // Make renderer const renderer = new THREE.WebGLRenderer({ alpha: true }); // Make transparent renderer.setClearColor(0xffffff, 0); // Set it to window size renderer.setSize(window.innerWidth, window.innerHeight); // Force shadows renderer.shadowMap.enabled = true; // Helper (optional) // const camera_helper = new THREE.CameraHelper(dirLight.shadow.camera); // scene.add(camera_helper); // Double quality const quality = 2; renderer.setSize(window.innerWidth * quality, window.innerHeight * quality, false); // Add mouse movement const controls = new OrbitControls(camera, renderer.domElement); // Add floor (plane) const plane_geometry = new THREE.PlaneGeometry(10, 10); const plane_material = new THREE.MeshPhongMaterial({ color: 0xffff00, side: THREE.DoubleSide }); const plane = new THREE.Mesh( plane_geometry, plane_material ); plane.rotation.x = 1.5708; plane.castShadow = true; plane.receiveShadow = true; scene.add(plane); console.log(plane); // Import glTF loader.load(path_to_model, function (gltf) { //gltf.scene.position.y = -5; //gltf.scene.center(); //gltf.scene.scale.set(0.1, 0.1, 0.1); // Make it cast shadows gltf.scene.castShadow = true; gltf.scene.traverse(function(node) { if (node.isMesh) { node.castShadow = true; //node.receiveShadow = true; } }); console.log(gltf); console.log('Adding glTF model to scene...'); scene.add(gltf.scene); console.log('Model added.'); console.log('Moving camera 5z...'); camera.position.z = 5; console.log('Camera moved.'); }, undefined, function (error) { console.error(error); }); container.appendChild(renderer.domElement); function animate() { requestAnimationFrame(animate); renderer.render(scene, camera); } animate(); </script> </head> <body> <div id="container"> </div> </body> </html>
更新:根据@Justin的建议,已将地面材质从
MeshBasicMaterial更换为MeshPhongMaterial。
解决方案
按以下步骤修改即可正常显示阴影:
- 调整平行光位置
你当前的平行光从Z轴正方向水平照射,阴影会被模型本身完全遮挡。把灯光位置改为带Y轴高度的斜向坐标,比如:
dirLight.position.set(10, 20, 10);
- 配置阴影贴图分辨率
默认的阴影贴图分辨率很低,增加配置提升清晰度:
dirLight.shadow.mapSize.width = 2048; dirLight.shadow.mapSize.height = 2048;
- 调整模型位置避免穿模
你注释了模型的位置偏移代码,glTF模型默认原点与地面Y=0重合会导致底部穿插,阴影无法正常显示。在模型加载完成后增加一行偏移:
gltf.scene.position.y = 0.1;
- 设置柔和阴影(可选)
可以给渲染器配置更自然的阴影类型:
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
内容的提问来源于stack exchange,提问作者LuisAFK
相关产品推荐
相关产品推荐

