3D model not casting shadows in ThreeJS

I am importing a glTF model into ThreeJS and have a PlaneGeometry acting as a ground. I need the model to cast shadows onto the plane/ground.

enter image description here

enter image description here

I’ve tried enabling

renderer.shadowMap.enabled = true;

on

const renderer = new THREE.WebGLRenderer({ alpha: true });

I also have 2 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 );

Final code

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/[email protected]/build/three.module.js';

  import { GLTFLoader } from 'https://cdn.skypack.dev/[email protected]/examples/jsm/loaders/GLTFLoader.js';    // for .glb and .gltf.glb
  // import { OBJLoader } from 'https://cdn.skypack.dev/[email protected]/examples/jsm/loaders/OBJLoader.js';   // for .obj

  import { OrbitControls } from 'https://cdn.skypack.dev/[email protected]/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>

Update: changed MeshBasicMaterial to MeshPhongMaterial in plane as suggested by @Justin.

Answer

The hemisphere light seems to be the main culprit. If you change the position of it you can start getting shadow. Most likely it was casting light everywhere thus drowning out any chance of shadows. Moving it away from the plane helped. Also the dirLight.shadow.camera.top/bottom/left/right are all too high. I used 2, vice 200, along with changing the hemi light and it seems to be working.

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>
</head>

<body>

  <div id="container">

  </div>
</body>

</html>

CSS

body
{
  margin: 0px;
  padding: 0px;
}

div#container canvas
{
  cursor: grab;
}

div#container canvas:active
{
  cursor: grabbing;
}

JS

import * as THREE from "https://cdn.skypack.dev/[email protected]/build/three.module.js";

import { GLTFLoader } from "https://cdn.skypack.dev/[email protected]/examples/jsm/loaders/GLTFLoader.js"; // for .glb and .gltf.glb
// import { OBJLoader } from 'https://cdn.skypack.dev/[email protected]/examples/jsm/loaders/OBJLoader.js';   // for .obj

import { OrbitControls } from "https://cdn.skypack.dev/[email protected]/examples/jsm/controls/OrbitControls.js";

const container = document.querySelector("div#container");

const path_to_model =
  "https://s3-us-west-2.amazonaws.com/s.cdpn.io/1376484/stacy_lightweight.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(20, 20, 10);
scene.add(hemiLight);
const dirLight = new THREE.DirectionalLight(0xffffff);
dirLight.position.set(-10, 20, 6);
dirLight.castShadow = true;
dirLight.shadow.camera.top = 2;
dirLight.shadow.camera.bottom = -2;
dirLight.shadow.camera.left = -2;
dirLight.shadow.camera.right = 2;
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);

//scene.add(box)

// 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();