Shadow on itself GLTF model

Can a GLTF gets his own shadow.

For example an animation of a Terrain and a Plane where both are from the same GLTF model.

Can this plan represent a shadow on the terrain where it’s passing above?

How does it look like if you do this in your onLoad() callback?

gltf.scene.traverse( function ( object ) {

    if ( object.isMesh ) {

        object.castShadow = true;
        object.receiveShadow = true;

    }

} );

With this, it is possible to implement self-shadowing. If you need an example, check:

https://threejs.org/examples/webgl_loader_fbx

1 Like

I already have this settings added on this code:

import * as THREE from 'https://threejs.org/build/three.module.js';

import { GLTFLoader } from 'https://threejs.org/examples/jsm/loaders/GLTFLoader.js';
import {GUI} from 'https://threejsfundamentals.org/threejs/../3rdparty/dat.gui.module.js';
import { RGBELoader } from '../loaders/RGBELoader.js';
import { RoughnessMipmapper } from '../loaders/RoughnessMipmapper.js';
import { EffectComposer } from 'https://threejs.org/examples/jsm/postprocessing/EffectComposer.js';
import { RenderPass } from 'https://threejs.org/examples/jsm/postprocessing/RenderPass.js';


let camera, scene, renderer, clock, mixer, composer, controls;

init();
render();

function init() {
  clock = new THREE.Clock();

  const canvas = document.querySelector('#c');

  camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 0.25, 20 );
  camera.position.set( -0.05, 0.37, 0.29 );
  camera.lookAt(new THREE.Vector3(-0.05,-0.12,-0.15));


  scene = new THREE.Scene();

 // scene.fog = new THREE.Fog( 0xffffff, 0.1, 2.55 );



  var ambientLight = new THREE.AmbientLight( 0xffffff, 0.15 );
  scene.add( ambientLight );

  var spotLight = new THREE.SpotLight( 0xffffff, 0.2 );
  spotLight.position.set( -10, 7, 6.6 );
  spotLight.angle = 0.4;
  spotLight.penumbra = 0.05;
  spotLight.decay = 1;
  spotLight.distance = 2000;

  spotLight.castShadow = true;
  spotLight.shadow.mapSize.width = 2024;
  spotLight.shadow.mapSize.height = 2024;
  spotLight.shadow.camera.near = 0.01;
  spotLight.shadow.camera.far = 1;

  scene.add( spotLight );

  spotLight.target.position.set( 3, 0, - 3 );
  scene.add( spotLight.target );


  new RGBELoader()
    .setDataType( THREE.UnsignedByteType )
    .setPath( '/assets/threeJS/map/gltf/' )
    .load( 'GCanyon_C_YumaPoint_3k.hdr', function ( texture ) {

      const envMap = pmremGenerator.fromEquirectangular( texture ).texture;

      scene.background = envMap;
      scene.environment = envMap;

      texture.dispose();
      pmremGenerator.dispose();

      render();

      const roughnessMipmapper = new RoughnessMipmapper( renderer );

      const loader = new GLTFLoader();
      loader.load( 'assets/threeJS/map/gltf/scene.gltf', function ( gltf ) {

       mixer = new THREE.AnimationMixer( gltf.scene );

        let model = gltf.scene;

        model.traverse( function ( child ) {

          if ( child.isMesh ) {

            child.material.shading = THREE.SmoothShading;
            child.castShadow = true;
            child.receiveShadow = true;

          }

        });

        scene.add(model);

        mixer = new THREE.AnimationMixer( gltf.scene );

        gltf.animations.forEach( ( clip ) => {

          mixer.clipAction( clip ).play();

        } );

        scene.add( gltf.scene );

        roughnessMipmapper.dispose();

        render();

      } );

    } );

  renderer = new THREE.WebGLRenderer( {canvas, antialias: true } );
  renderer.gammaOutput = true;
  renderer.gammaFactor = 2.2;
  renderer.shadowMap = THREE.VSMShadowMap;

  const renderScene = new RenderPass( scene, camera );

  composer = new EffectComposer( renderer );
  composer.addPass( renderScene );



  renderer.setPixelRatio( window.devicePixelRatio );
  renderer.setSize( window.innerWidth, window.innerHeight );

  renderer.toneMapping = THREE.ACESFilmicToneMapping;
  renderer.toneMappingExposure = 1;
  renderer.outputEncoding = THREE.sRGBEncoding;

  const pmremGenerator = new THREE.PMREMGenerator( renderer );
  pmremGenerator.compileEquirectangularShader();

  const gui = new GUI();

  gui.add(camera.position, 'x', -5,5).step(0.001).onChange( function( value ){ camera.position.z = value; } );
  gui.add(camera.position, 'y', -5,5).step(0.001).onChange( function( value ){ camera.position.z = value; } );
  gui.add(camera.position, 'z', 1,5).step(0.001).onChange( function( value ){ camera.position.z = value; } );


  window.addEventListener( 'resize', onWindowResize, false );

}

function onWindowResize() {

  camera.aspect = window.innerWidth / window.innerHeight;
  camera.updateProjectionMatrix();

  renderer.setSize( window.innerWidth, window.innerHeight );

  render();

}

function render() {

  var delta = clock.getDelta();
  controls.update();
  if ( mixer ) mixer.update( delta );

  renderer.render( scene, camera );

  requestAnimationFrame( render );


  composer.render();
}

And I am unable to see any shadows

Here you can see the it closer:

@tizkon

Do you have

renderer.shadowMap.enabled = true

In this code?

No I don’t but I had in the past and it didn’t worked but I should had other config and now it’s working.

First at all many thanks, I was struggling with all this configs but now the shadows are making many lines on the map and I don’t know how to solve it’s probably a numeric term:

My code is now:

import * as THREE from 'https://threejs.org/build/three.module.js';

import {GUI} from 'https://threejsfundamentals.org/threejs/../3rdparty/dat.gui.module.js';
import { OrbitControls } from 'https://threejs.org/examples/jsm/controls/OrbitControls.js';
import { GLTFLoader } from 'https://threejs.org/examples/jsm/loaders/GLTFLoader.js';
import { RGBELoader } from 'https://threejs.org/examples/jsm/loaders/RGBELoader.js';
import { RoughnessMipmapper } from 'https://threejs.org/examples/jsm/utils/RoughnessMipmapper.js';

var container, controls,pmremGenerator;
var camera, scene, renderer, mixer, clock;

init();
animate();

function init() {

  const canvas = document.querySelector('#c');

  scene = new THREE.Scene();



  camera = new THREE.PerspectiveCamera( 46, window.innerWidth / window.innerHeight, 0.01, 20 );
  camera.position.set( -0.05, 0.37, 0.29 );
  camera.lookAt(new THREE.Vector3(-0.05,-0.12,-0.15));



  clock = new THREE.Clock();


  var  hemiLight = new THREE.HemisphereLight( 0x0000ff, 0x00ff00, 1 );
  scene.add(hemiLight);

  var spotLight = new THREE.SpotLight( 0xffffff, 0.85 );
  spotLight.position.set( 1, 1, -1 );
  spotLight.angle = 0.5;
  spotLight.penumbra = 0.05;
  spotLight.decay = 1;
  spotLight.distance = 20;
  spotLight.castShadow = true;
  spotLight.shadow.mapSize.width = window.innerWidth;
  spotLight.shadow.mapSize.height = window.innerHeight;
  spotLight.shadow.camera.near = 0.1;
  spotLight.shadow.camera.far = 2;

  scene.add( spotLight );

  spotLight.target.position.set( 0, 0, 0 );
  scene.add( spotLight.target );


  new RGBELoader()
    .setDataType( THREE.UnsignedByteType )
    .setPath( '/assets/threeJS/map/gltf/' )
    .load( 'GCanyon_C_YumaPoint_3k.hdr', function ( texture ) {

      const envMap = pmremGenerator.fromEquirectangular( texture ).texture;

      scene.background = envMap;
      scene.environment = envMap;

      texture.dispose();
      pmremGenerator.dispose();

      animate();

      const roughnessMipmapper = new RoughnessMipmapper( renderer );

      var loader = new GLTFLoader();
      loader.load( 'assets/threeJS/map/gltf/scene.gltf', function ( gltf ) {

        mixer = new THREE.AnimationMixer( gltf.scene );

        let model = gltf.scene;

        model.traverse( function ( child ) {

          if ( child.isMesh ) {

            child.material.shading = THREE.SmoothShading;
            child.castShadow = true;
            child.receiveShadow = true;

          }

        });

        scene.add(model);

        gltf.animations.forEach( ( clip ) => {

          mixer.clipAction( clip ).play();

        } );

      } );

    } );



  renderer = new THREE.WebGLRenderer( {canvas, antialias: true } );
  renderer.setPixelRatio( window.devicePixelRatio );
  renderer.setSize( window.innerWidth, window.innerHeight );
  renderer.toneMapping = THREE.ReinhardToneMapping;
  renderer.toneMappingExposure = 1;
  renderer.shadowMap.enabled = true;
  renderer.receiveShadow = true;
  renderer.shadowMap.type = THREE.PCFShadowMap;

  const pmremGenerator = new THREE.PMREMGenerator( renderer );
  pmremGenerator.compileEquirectangularShader();

  controls = new OrbitControls( camera, renderer.domElement );

  camera.position.set( -0.05, 0.37, 0.29 );

  camera.lookAt(new THREE.Vector3(-0.05,-0.12,-0.15));



  window.addEventListener( 'resize', onWindowResize, false );
}

function onWindowResize() {

  camera.aspect = window.innerWidth / window.innerHeight;
  camera.updateProjectionMatrix();

  renderer.setSize( window.innerWidth, window.innerHeight );

}



function animate() {



  var delta = clock.getDelta();
  controls.update();
  if ( mixer ) mixer.update( delta );

  renderer.render( scene, camera );

  requestAnimationFrame( animate );
}

Now its looks much better than before but also the performance of the browser is much worse. Could it be cause this problem with Shadows?

Greetings

Now I solved it settling that bias:

spotLight.shadow.bias = -0.0005;

But now the performance is still bad and lagging

One important detail is that the Animation file is at 60 fps exported from blender

@tizkon
Have you tried using directional light instead of spotlight?

Yes, and still the same problem:

I got new info regarding my problem on this post

But If I remove the shadow casting of my own file, I lost all the shadows so I can’t go this way.

Now I am learning about how to change the frustum of the camera that is what @Mugen87 told in that forum post.

@tizkon
laggy and lines?

have you considered removing the hemisphere light seeing as you’ve got a hdr in there anyway, if your hdr is 3k as it says it is i think using that as env map will have to make a lot of calculations, might need to use one or the other?

Yes i just trid with one of 1k

The performance became a bit better not a big change

I tried this one: HDRI: Kloppenheim 06 | HDRI Haven

your camera far clipping plane is only set to 20 which seems reasonable but you shadow camera.far is set to 2 which seems big enough to cover the entire scene with shadows right? can always add the light helper to see if the bounds of the shadow camera are too big and adjust camera far clip to a similar value…

just out of curiosity, did your directional light have a setup like this?..

directionalLight = new THREE.DirectionalLight( 0xffffff, 0.7, 18 );
directionalLight.position.set(1, 1, -1);
directionalLight.castShadow = true;
directionalLight.shadow.camera.left = - 3;
directionalLight.shadow.camera.right = 3;
directionalLight.shadow.camera.top = 3;
directionalLight.shadow.camera.bottom = - 3;
directionalLight.shadow.camera.fov = 45;
directionalLight.shadow.camera.aspect = 1;
directionalLight.shadow.camera.near = 0.1;
directionalLight.shadow.camera.far = 3;
directionalLight.shadow.bias = -0.001;
directionalLight.shadow.mapSize.width = 2048;
directionalLight.shadow.mapSize.height = 2048;

scene.add( directionalLight );

No mine looks like this:

var directionalLight = new THREE.DirectionalLight( 0xffffff, 0.95 );
directionalLight.position.set( 1, 0.5, -0.6 );
directionalLight.castShadow = true;
directionalLight.shadow.bias = -0.0001;
directionalLight.shadow.mapSize.width = window.innerWidth;
directionalLight.shadow.mapSize.height = window.innerHeight;
directionalLight.shadow.camera.near = 0.1;
directionalLight.shadow.camera.far = 2;

scene.add( directionalLight );

Also I tried your’s and that was the result:

Now its likes better but its still laggy.

It’s all throught Shadow, I should optimize this I think.

do you want/need the actual lighting from the hdr? because you could try removing this line, should increse performance but then again i’ve used hdr in conjunction with directional light before here xenzvr.com and seems to run ok on medium teir devices, bit slower on older ones like iphone 6s for example but rapid on my p30

@tizkon

yeah sorry you can try setting

directionalLight.shadow.bias = 0.001;

i put a negative number by accident

and try add

directionalLight.shadow.normalBias = 0.003;

Just tested this settings and still near the same result:

The code looks like this now:

import * as THREE from 'https://threejs.org/build/three.module.js';

import {GUI} from ‘https://threejsfundamentals.org/threejs/../3rdparty/dat.gui.module.js’;
import { OrbitControls } from ‘https://threejs.org/examples/jsm/controls/OrbitControls.js’;
import { GLTFLoader } from ‘https://threejs.org/examples/jsm/loaders/GLTFLoader.js’;
import { RGBELoader } from ‘https://threejs.org/examples/jsm/loaders/RGBELoader.js’;
import { RoughnessMipmapper } from ‘https://threejs.org/examples/jsm/utils/RoughnessMipmapper.js’;

var container, controls,pmremGenerator;
var camera, scene, renderer, mixer, clock;

init();
animate();

function init() {

const canvas = document.querySelector(’#c’);

scene = new THREE.Scene();

camera = new THREE.PerspectiveCamera( 46, window.innerWidth / window.innerHeight, 0.01, 20 );
camera.position.set( -0.05, 0.37, 0.29 );
camera.lookAt(new THREE.Vector3(-0.05,-0.12,-0.15));

clock = new THREE.Clock();

//var hemiLight = new THREE.HemisphereLight( 0x0000ff, 0x00ff00, 1 );
//scene.add(hemiLight);

let directionalLight = new THREE.DirectionalLight( 0xffffff, 0.7, 18 );
directionalLight.position.set(1, 1, -1);
directionalLight.castShadow = true;
directionalLight.shadow.camera.left = - 3;
directionalLight.shadow.camera.right = 3;
directionalLight.shadow.camera.top = 3;
directionalLight.shadow.camera.bottom = - 3;
directionalLight.shadow.camera.fov = 45;
directionalLight.shadow.camera.aspect = 1;
directionalLight.shadow.camera.near = 0.1;
directionalLight.shadow.camera.far = 3;
directionalLight.shadow.bias = 0.001;
directionalLight.shadow.normalBias = 0.003;
directionalLight.shadow.mapSize.width = 2048;
directionalLight.shadow.mapSize.height = 2048;

scene.add( directionalLight );

scene.add( directionalLight );

new RGBELoader()
.setDataType( THREE.UnsignedByteType )
.setPath( ‘/assets/threeJS/map/gltf/’ )
.load( ‘kloppenheim_06_1k.hdr’, function ( texture ) {

  const envMap = pmremGenerator.fromEquirectangular( texture ).texture;

  scene.background = envMap;
  scene.environment = envMap;

  texture.dispose();
  pmremGenerator.dispose();

  animate();


  var loader = new GLTFLoader();
  loader.load( 'assets/threeJS/map/gltf/scene.gltf', function ( gltf ) {

    mixer = new THREE.AnimationMixer( gltf.scene );

    let model = gltf.scene;

    model.traverse( function ( child ) {

      if ( child.isMesh ) {

        child.material.shading = THREE.SmoothShading;
        child.castShadow = true;
        child.receiveShadow = true;
        child.material.metalness = 1;
        child.material.roughness = 1;
      }

    });

    scene.add(model);

    gltf.animations.forEach( ( clip ) => {

      mixer.clipAction( clip ).play();

    } );

  } );

} );

renderer = new THREE.WebGLRenderer( {canvas, antialias: true } );
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
renderer.toneMapping = THREE.ReinhardToneMapping;
renderer.toneMappingExposure = 1;
renderer.shadowMap.enabled = true;
renderer.shadowMap.autoUpdate = true;
renderer.receiveShadow = true;
renderer.gammaFactor = 2.2;
renderer.shadowMap.type = THREE.PCFShadowMap;
//renderer.outputEncoding = THREE.sRGBEncoding;

const pmremGenerator = new THREE.PMREMGenerator( renderer );
pmremGenerator.compileEquirectangularShader();

controls = new OrbitControls( camera, renderer.domElement );

camera.position.set( -0.05, 0.37, 0.29 );

camera.lookAt(new THREE.Vector3(-0.05,-0.12,-0.15));

window.addEventListener( ‘resize’, onWindowResize, false );
}

function onWindowResize() {

camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();

renderer.setSize( window.innerWidth, window.innerHeight );

}

function animate() {

var delta = clock.getDelta();
controls.update();
if ( mixer ) mixer.update( delta );

renderer.render( scene, camera );

requestAnimationFrame( animate );
}

Now I tried to reduce the pixel ratio of my scene this way:

renderer.setPixelRatio( window.devicePixelRatio*0.8 );

And now its looks more smooth and also it has like a small blur and a tunnel effect on the camera:

Also I would be good to have a good performance with the 100% of the pixel ratio.

@tizkon
do you have a live link to share as a codepen or something? that would be easier to help you out i think…

@tizcon
here’s a simple demo of a shadow setup, maybe looking at this can help?

1 Like