You need to enable JavaScript to run this app. ThreeJS中导入的glTF模型无法向PlaneGeometry地面投射阴影
优惠活动
大模型
产品
解决方案
定价
更多

ThreeJS中导入的glTF模型无法向PlaneGeometry地面投射阴影

ThreeJS中glTF模型无法向平面地面投射阴影问题

我正在将glTF模型导入ThreeJS项目,使用PlaneGeometry创建平面作为地面,需要实现模型向地面投射阴影的效果。
示例图1
示例图2

我已经尝试开启渲染器的阴影映射配置:

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。


解决方案

按以下步骤修改即可正常显示阴影:

  1. 调整平行光位置
    你当前的平行光从Z轴正方向水平照射,阴影会被模型本身完全遮挡。把灯光位置改为带Y轴高度的斜向坐标,比如:
dirLight.position.set(10, 20, 10);
  1. 配置阴影贴图分辨率
    默认的阴影贴图分辨率很低,增加配置提升清晰度:
dirLight.shadow.mapSize.width = 2048;
dirLight.shadow.mapSize.height = 2048;
  1. 调整模型位置避免穿模
    你注释了模型的位置偏移代码,glTF模型默认原点与地面Y=0重合会导致底部穿插,阴影无法正常显示。在模型加载完成后增加一行偏移:
gltf.scene.position.y = 0.1;
  1. 设置柔和阴影(可选)
    可以给渲染器配置更自然的阴影类型:
renderer.shadowMap.type = THREE.PCFSoftShadowMap;

内容的提问来源于stack exchange,提问作者LuisAFK

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.14 06:27:03