Identifying particular surfaces to add shadows in three js

I’m learning three js recently and I’m trying to mimic the scene which is present in below link by downloading the model
https://sketchfab.com/3d-models/ccity-building-set-1-a2d5c7bfcc2148fb8994864c43dfcc97

I’ve downloaded gltf model and tried to add shadows to all the children in the scene and it came like this. It’s not looking good and all the road surfaces also have shadows

So I’ve checked the details in gltf scene. and I’ve applied shadows only to surfaces which is not ConcreteSurfaces. Now it looks like this. Still surfaces on buildings are getting shadows and some of the buildings shadows are missing

Below is my code which I’m using.

var scene = new THREE.Scene();
        const modelUrl = new URL('./assets/ground/scene.gltf', import.meta.url);
        var camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
        var renderer = new THREE.WebGLRenderer({antialias:true});
        renderer.setSize(window.innerWidth, window.innerHeight);
        // renderer.setClearColor(0xA3A3A3);
        renderer.shadowMap.enabled = true;
        renderer.shadowMap.type = THREE.PCFSoftShadowMap; // default THREE.PCFShadowMap
        renderer.gammaOutput = true;
        renderer.gammaFactor = 2.2;

        refContainer.current && refContainer.current.appendChild(renderer.domElement);

        const orbit = new OrbitControls(camera, renderer.domElement);
        camera.position.set(100, 100, -100);

        // camera.updateMatrix();
        orbit.update();


        const ambientLight = new THREE.AmbientLight(0x333333);
        scene.add(ambientLight);

        var spotLight = new THREE.SpotLight( 0xFBCEB1, 10000 );
        spotLight.position.set( -500, 200, 100 );
        spotLight.angle = 0.4;
        spotLight.penumbra = 0.05;
        spotLight.decay = 1;
        spotLight.distance = 2000;

        spotLight.castShadow = true;
        scene.add( spotLight );

        spotLight.target.position.set( 3, 0, - 3 );
        scene.add( spotLight.target );
        const helper = new THREE.DirectionalLightHelper( spotLight, 5 );
        scene.add( helper );


        const assetLoader = new GLTFLoader();


        assetLoader.load(modelUrl.href, function(gltf) {
            const model = gltf.scene;
            model.scale.set(0.01,0.01,0.01);
            model.position.z = 100
            model.position.x = 100
            model.traverse( function( child ) { 

                if ( child.isMesh ) {
                    console.log(child)
                    var childName = child.name
                    if(!childName.includes('ConcreteBlock')) {
                        child.castShadow = true;
                        child.receiveShadow = true;
                    }


                }

            } );
            scene.add(model);

        }, undefined, function(error) {
            console.error(error);
        });


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

        renderer.setAnimationLoop(animate);
        animate();

How to identify the surfaces which needs shadows so that the scene looks like in the above link??? Am i doing it in right way??

The first image just looks like the shadow.bias needs adjustment.

https://threejs.org/docs/#api/en/lights/shadows/LightShadow.bias

What you’re seeing is called “shadow acne” and is a common problem with shadowmapping, that can be improved in a few different ways… the first being to adjust the .bias by a small amount + or -.

We need Directional Light to have Light Shadow.

If I use Directional Light, I’m not getting any shadows. Even thought the whole gltf is made of Mesh Standard Material, None of the meshes are getting shadows with Directional Light. Don’t know why. I’ve seen others facing the same issue here.
https://discourse.threejs.org/t/directional-light-not-showing-shadows/6541
So I’m trying with spotlight. But Spotlight doesn’t have bias parameter.

This is how it looks like if i’m using Directional Light.

You see the little dark square where the white line is intersecting the ground?

That is your entire shadowmap. You need to increase the coverage of the shadowmap.

Set up your directionalLight like this:


const dirLight = new THREE.DirectionalLight('white',0.9)
dirLight.castShadow = true
dirLight.shadow.mapSize.width = 2048
dirLight.shadow.mapSize.height = 2048
dirLight.shadow.camera.top = dirLight.shadow.camera.right = 200.;
dirLight.shadow.camera.far = 1500;
dirLight.shadow.camera.bottom = dirLight.shadow.camera.left = -200.;
dirLight.shadow.camera.updateProjectionMatrix();
scene.add(dirLight)

the left/right/top/bottom control how wide an area the shadowmap will cover.

So if your scenery is 200 units across… then left/right/top/bottom set to -100 → +100 should cover the entire scene… so you may have to adjust those numbers.

The
dirLight.shadow.mapSize.width / height = 2048
Controls how detailed the shadow will be. a mapsize of 128 will be very blurry… 2048 will be very crisp but much slower to render.
You want to find the smallest values for mapSize and camera left/right/top bottom that still give you the coverage you need and the detail you need, but still perform well enough to be usable.

1 Like

Pretty much everyone goes through this same arc when dealing with shadows so don’t get frustrated! It’s tricky to get this all dialled right…
there are also different shadow mapping types that can help… for instance Cascaded shadowmaps can give you better shadow detail up close, at an increased cost in rendering.
PCFSoftShadowMaps look better than PCFShadowMaps but cost a bit more to render…

DirectionalLight shadows only cost 1 render past to generate the shadowmap.

PointLights cost 6 render passes since they generate a cubemap.

There are a lot of nuances to it.

Thank you so much… It worked.
I’ve added bias also. Now it looks like this.

But Textures got too soft. I’ll adjust the params to match the contrast.

Niice. Looking good.

Also for outdoor scenes, you often want some AmbientLight too, since outdoor scenes have lots of reflected light that makes the dark areas not pitch black.

And yeah for outdoor lighting scenarios like this, Cascaded shadowmaps can also be a good choice since they concentrate detail (“sharpness”) near the camera, while still covering a large area. (but at a higher rendering cost)

https://threejs.org/examples/webgl_shadowmap_csm.html