Shadows are not being projected from the 3D model in the ThreeJS

I'm currently bringing in a glTF model into ThreeJS and utilizing a PlaneGeometry as the ground. My goal is to have the model project shadows onto the plane/ground.

https://i.sstatic.net/JMyZz.png

https://i.sstatic.net/kIIOsm.png

I've attempted to enable

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>Importing 3D Model into Three JS</title>
  
  <script type="module" defer>
  import * as THREE from 'https://cdn.skypack.dev/<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="61ddcab4cdcdafd9d7ccc4dddcc7cbd5dbcb86cdc1c3">[email protected]</a>/build/three.module.js';

  import { GLTFLoader } from 'https://cdn.skypack.dev/<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="16627e64737356263827242f3826">[email protected]</a>/examples/jsm/loaders/GLTFLoader.js';    // for .glb and .gltf.glb
  // import { OBJLoader } from 'https://cdn.skypack.dev/<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="8cf8e4fee9e9ccbca2bdbeb5a2bc">[email protected]</a>/examples/jsm/loaders/OBJLoader.js';   // for .obj

  import { OrbitControls } from 'https://cdn.skypack.dev/<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="32465a40575772021c03000b1c02">[email protected]</a>/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>

Updated: swapped out MeshBasicMaterial with MeshPhongMaterial on the plane following advice from @Justin.

Answer №1

The issue mainly lies with the hemisphere light. By adjusting its position, shadows can start appearing. It seems that previously, the light was too overpowering and prevented any shadows from being visible. Moving it further away from the plane resolved this issue. Additionally, the values for

dirLight.shadow.camera.top/bottom/left/right
were set too high initially. I adjusted them to 2 instead of 200, along with tweaking the hemisphere light, which resulted in the desired effect.

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

// Custom JavaScript implementation goes here...

// Your unique implementations can be added here

// Ensure to tailor the JavaScript according to your specific requirements and functionalities

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Persist in the face of a mishap in javascript

Two scripts are present on the page. If the first script encounters an error, the second script will not function properly as a result. Is there a way to make the second script ignore any errors from the first one and still work? Please note that I am str ...

Switch between different tables

I need assistance with implementing a functionality where there are two tables displaying different data and two buttons at the top of the page. Upon clicking one button, I want Table A to be displayed, while clicking on the other button will hide Table ...

How do we insert an element into an array with a fixed memory size?

Recently, I reached the Algorithm section of JavaScript and learned that an array cannot grow. However, I am confused about how we can push items into an array. Is the "array" copied and new memory added for additional items? ...

Interactive 3D Tooltip with Three.js

While I haven't found any solutions yet, I am attempting to create a tooltip for mouse hover using a perspective camera. Here is the issue with the tooltip: https://jsfiddle.net/thehui87/d12fLr0b/14/ This code is based on threejs r76: function onDo ...

Rendering a component inside tabs in React based on a certain condition

I'm working on creating an admin panel for managing a hotel backend system One of the tasks I need to accomplish is checking the verified status and rendering accordingly Here's what I have implemented so far const AdminDashboard = () => { ...

Adding a line break using the character in a JavaScript template string that is conditionally rendered within a React Bootstrap Button component does not work

One of the React components I've created includes a React Bootstrap Button component. Inside this component, I conditionally render a string based on its toggled state, shown in the code snippet below: <Button size="sm" ...

Steps to reset default behavior after using event.preventDefault()

I came across a similar question on this post, but the solution provided did not work for my specific needs. So, I decided to share some example code and provide a bit of explanation... $(document).keypress( function (event) { // Pressing Up o ...

when within a 'for in' loop

Querying Object Equivalence: I find myself struggling to comprehend the flow of my code and how I can rectify it. Within the areEqual function, I am comparing the "value" property of two objects. If they match -> isEqual=true -> continue with the ...

What is the best way to modify the width of an option box separately from the select width using jquery?

Is there a way to adjust the width of an option box separately from the select width? Here's what I've tried: $('option').css({'width': 100}); The select element needs to have a width of 400px; select{ width:400px; } Link ...

Convert an array into individual objects and include key-value pairs from a separate object

I am working with two arrays that have the same length: One is a simple array numbers = [4,5,6] The other is an array of objects objects = [ {type: "x", value: 7}, {type: "y", value: 8}, {type: "z", value: 9} ] My goal is to combine th ...

Verify whether the message received contains a specific text within the property

Currently, I am facing a challenge with displaying a specific div in my component template only if any incoming messages contain the TYPE_OTHER property. With numerous variations of the TYPE_OTHER identifier, I am pondering on creating a condition that can ...

Error message: Unable to locate module (webpack)/hot/emitter when running ionic serve with Ionic 4

Current Technology Stack: Node v10.15.1 Ionic 4.10.1 Upon running ionic serve, an error is encountered: ERROR in (webpack)/hot/emitter.js [ng] Module not found: Error: Can't resolve 'events' in '/zazou/node_modules/@angular-de ...

Implementing a JSON query with an onkeyup event listener

My main objective with this code is to trigger a json query request by using a textfield to specify the location. It's essentially a quick search feature that searches for specific content on a different website. For example, if I input www.calsolum/ ...

Extract information from an array located inside a nested object

My aim is to calculate the length of the skills array for each user individually. To begin, I have this JSON data: const txt = `{ "Alex": { "email": "<a href="/cdn-cgi/l/email-protection" class="__cf_email__" da ...

Limit access to the server in Node.js by allowing loading only if the request is coming from another page and form POST variables are present

After searching for documentation without success, I have a question: Is there a way to prevent users from directly accessing my node.js server at website.com:3000? Currently, when a user visits my node.js website, it crashes the whole server and takes i ...

The show.bs.modal event has been triggered when the .show() method is used on elements within the

Recently, I discovered that the event show.bs.modal is triggered not only when the modal itself is shown, but also every time you call the .show() method for an element within the modal. To attach the event handler, you would typically use the following c ...

What is the process for refreshing HTML elements that have been generated using information from a CSV document?

My elements are dynamically generated from a live CSV file that updates every 1 minute. I'm aiming to manage these elements in the following way: Remove items no longer present in the CSV file Add new items that have appeared in the CSV file Maintai ...

The getJSON method returns a status code of 200, however, it does

After researching extensively, I have yet to find a solution that addresses my specific issue. The core of the problem lies in a piece of PHP code responsible for generating a JSON object. This is what my page currently looks like: <?php $supps = ...

I'm wondering if affdex-sdk can be utilized in node.js to store emotion data in mongodb

Is it possible to utilize the affdex Javascript SDK with Node.js for saving emotion data to MongoDB? Can the emotion data collected on the frontend be passed to the backend (Node.js) for storage in MongoDB? The CameraDetector constructor requires four pa ...

What is limiting me from utilizing the entire Google Calendar feed?

I currently have a public Google Calendar set up. My goal is to retrieve appointment data in JSON format from this calendar. When I utilize the following URL https://www.google.com/calendar/feeds/{calendar_id}%40group.calendar.google.com/public/basic?alt ...