Blender-generated GLB file loaded with GLTFLoader has lights that can’t cast shadows

Description of the problem

I have a card with a light shining on it. There’s also a plane behind that card and I want it to receive the card’s shadow. I’ve done my best to create a minimal example. There are two problems.

Problem 1

After loading the glTF file with the GLTFLoader, I traverse all objects and set castShadow and receiveShadow to true. The shadow doesn’t appear. However, if I create a new PointLight, the shadow appears. It seems that the light that from the glTF file can’t cast a shadow for some reason. Why?

Problem 2

In Blender, I’ve set up a camera that looks at the scene. When I try to render with it, I see nothing. Why does that happen? The only workaround is to create a new camera and point it manually.


Here’s my main script:

import * as THREE from '../build/three.module.js';

import { OrbitControls } from './jsm/controls/OrbitControls.js';
import { GLTFLoader } from './jsm/loaders/GLTFLoader.js';

var container, controls;
var camera, scene, renderer;

init();

function init() {

  container = document.createElement('div');
  document.body.appendChild(container);

  scene = new THREE.Scene();

  var loader = new GLTFLoader();
  loader.load('./models/card.glb', function (gltf) {

    // Problem 1: The only way to get a shadow is by creating a light in THREE, rather
    // than using the one from glTF.
    //
    // let light = new THREE.PointLight(0xffffff, 0.5)
    // light.position.set(0, 5, 3)
    // scene.add(light)

    camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.25, 20);
    camera.position.set(0, 0.2, 0.4);
    camera.rotation.set(10, 10, 10)

    // Problem 2: Doesn't work with the Blender camera
    // camera = gltf.cameras[0]

    scene.add(gltf.scene);

    controls = new OrbitControls(camera, renderer.domElement);
    controls.target.set(0, 0, - 0.2);
    controls.update();

    // Add shadows to all objects, including lights
    scene.traverse(function (obj) {
      obj.castShadow = true
      obj.receiveShadow = true
      console.log('setting shadow to', obj)
    })

    render();

  });

  renderer = new THREE.WebGLRenderer({ antialias: true });
  renderer.setPixelRatio(window.devicePixelRatio);
  renderer.setSize(window.innerWidth, window.innerHeight);
  renderer.outputEncoding = THREE.sRGBEncoding
  renderer.shadowMap.enabled = true
  container.appendChild(renderer.domElement);

}

function render() {
  renderer.render(scene, camera);
  window.requestAnimationFrame(render)
}

Here’s how the scene looks with the external PointLight:

image

Here’s how the scene looks without the external PointLight, i.e. using only the light from Blender:

image

There’s a shadow in the first image, and there’s no shadow in the second. I want to have a shadow without creating any new lights. I want to just load the .glb file, set castShadow and receiveShadow, and have the shadows appear.

Resources

I’m including a file with an ES6 modules example. I’ve included both the .glb file and .blend file there as well. Simply spin up a local server and open the examples/card.html file.

card-test.zip


Three.js version

I cloned this repo and put my experiment in the /examples folder.

Browser
  • All of them
  • Chrome
  • Firefox
  • Internet Explorer
OS
  • All of them
  • Windows
  • macOS
  • Linux
  • Android
  • iOS

Author: Fantashit

2 thoughts on “Blender-generated GLB file loaded with GLTFLoader has lights that can’t cast shadows

  1. If you reposition the light with light.position.y += 0.5 you’ll see the shadow, so I think this is a matter of configuring shadow settings for the relatively small scales involved. This change will show a shadow:

    scene.traverse(function (obj) {
      if (obj.isLight) {
        obj.shadow.camera.near = 0.001;
        obj.shadow.camera.updateProjectionMatrix();
      } else if (obj.isMesh) {
        obj.castShadow = true
        obj.receiveShadow = true
      } 
    })

    I also added a sphere to show the light’s position:

    Screen Shot 2020-02-05 at 9 21 37 AM

Comments are closed.