Subscribe to our free newsletter

To make sure you won't miss any valuable content we share with our community.

Three.js Realistic Shadows: A Practical Guide

In this tutorial, we want to create more realistic scenes by making the shadow effect. There are many parameters in creating a shadow of an object. One of them is the material. For instance, if you use the Mesh-basic material, you will see no shadow effects. The next important parameter is the light source. Different light sources create various types of shadows. Some of them do not generate any shadow effect on the object. Our blog has covered a complete article about the light sources in Three.js. Another element that can provide the shadow effect is the background scene. There is also another article about background scenes on our blog. Once you use the background for your scene, depending on the quality of the HDR image, the light source (for instance, the sun) will act as a light source like the point light or the rect-area light. Furthermore, we can use the shadow properties of the point light. This tutorial will test these properties and see their effects on a sphere object.

Getting started with the basics:

We will get started with the main elements of a Three js scene, including the camera, the renderer, the scene, and the object. Before doing that, we use the Vite plugin to easily create all the folders and files you need to run the Three.js code. First off, create a folder in the directory of your projects by using the following commands: mkdir Shadows
cd Shadows
Then, inside of the your project folder, create the necessary files and folders by simply running the Vite plugin command: npm create [email protected] Then enter the name of the project. You can write the name of your project as the name. And also the package (the name is arbitrary, and you can choose anything you want). Then select vanilla as the framework and variant. After that, enter the following commands in the terminal. Notice that here Shadows is the project folder’s name, and thus, we have changed the directory to Shadows. The name depends on the name you enter in the Vite plugin : cd Shadows
npm install
Afterward, you can enter the JavaScript code you want to write in the main.js file. So, we will enter the base or template code for running every project with an animating object, such as a sphere. Also, do not forget to install the Three.js package library every time you create a project: npm install three

The code:

Now, enter the following script in the main.js file:

import * as THREE from 'three';
import { OrbitControls } from '/node_modules/three/examples/jsm/controls/OrbitControls.js';
import Stats from '/node_modules/three/examples/jsm/libs/stats.module.js';

const scene = new THREE.Scene();
const light = new THREE.DirectionalLight();
light.castShadow = true;
light.shadow.mapSize.width = 512;
light.shadow.mapSize.height = 512;
light.shadow.camera.near = 0.05;
light.shadow.camera.far = 200;
scene.add(light);

const camera = new THREE.PerspectiveCamera(
     75,
     window.innerWidth / window.innerHeight,
     0.1,
     1000
)

camera.position.z = 7;
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
document.body.appendChild(renderer.domElement);
new OrbitControls(camera, renderer.domElement);

//creating the plane
const planeGeometry = new THREE.PlaneGeometry(20, 20);
const planeMaterial = new THREE.MeshPhongMaterial({
     map : new THREE.TextureLoader().load('./img/aerial_rocks_02_diff_4k.jpg') 
});

planeMaterial.displacementMap = new THREE.TextureLoader().load('./img/aerial_rocks_02_disp_1k.jpg');
planeMaterial.normalMap = new THREE.TextureLoader().load('./img/aerial_rocks_02_nor_dx_1k.jpg');

const plane = new THREE.Mesh(planeGeometry, planeMaterial );
plane.rotateX(-Math.PI / 2);
plane.position.y = -1.75;
plane.position.z = -2;
plane.receiveShadow = true;
scene.add(plane);

//creating the torus
const torusGeometry = new THREE.TorusGeometry(1, 0.3, 16, 100);
const material = new THREE.MeshStandardMaterial();
const torus = new THREE.Mesh( torusGeometry, material);
torus.position.x = 0;
torus.position.y = 0;
torus.castShadow = true;
torus.receiveShadow = true;
scene.add(torus);

window.addEventListener('resize', onWindowResize, false);
function onWindowResize() {
     camera.aspect = window.innerWidth / window.innerHeight;
     camera.updateProjectionMatrix();
     renderer.setSize(window.innerWidth, window.innerHeight);
     Render();
}

const stats = Stats();
document.body.appendChild(stats.dom);
function animate() {
     requestAnimationFrame(animate);
     torus.rotation.x += 0.02;
     render();
     stats.update();
}

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

animate();


Now if we save the code, and enter the following command in the terminal: npm run dev We should see the following result:

Which is a rotating torus with its shadow on the texture of the plane. You can rotate around the scene and see from other angles.

You can create a new folder called img and add any texture that you want in it. Notice that in the folder, you should have the normal map, the displacement map, and the texture photo, and also edit the names in the script according to the names of the texture images. If you do not want to have the texture, you can remove the map, displacement map, and normal map from the script. We have downloaded the texture of the above scene from polyhaven.com.

Explaining the code:

The first thing we did at the beginning was to import the necessary libraries. Then, we defined the scene, the point light, the camera, and the renderer. In the point light section of the code, we set the castShadow property to true. Afterward, we set the size and the distance of the shadow. Next, we enabled the shadowMap property for the renderer and specified its type to PCFSoftShadowMap.After that, we created a plane and set its position. We also set the receiveShadow property to true, So that we can see the shadows of the objects on it. We also created a torus and did the same as we did for the plane on it, in addition to setting the castShadow and receiveShadow properties to true so that it can cast the shadow and receive the shadow of itself. In the end, we wrote the animation code for the torus.

Conclusion

In this tutorial, we worked on different shadow properties of the point light, materials, the renderer, and the camera to be able to create the shadow effect. There are two types of shadow properties for the objects. One of them is the cast shadow property which will cast the shadow of the object on the ground, other objects, or itself (not all shapes cast shadows on themselves). And the other one is the receive shadow property that lets the object receive the shadows of other objects. Moreover, the renderer has different types of shadow mapping. In this tutorial, we only used the PCFSoftShadowMap . But, there are other types which you can find the complete list from the official website of Three.js.

Download this Article in PDF format

3d websites

Arashtad Custom Services

In Arashtad, we have gathered a professional team of developers who are working in fields such as 3D websites, 3D games, metaverses, and other types of WebGL and 3D applications as well as blockchain developemnet.

Arashtad Serivces
Drop us a message and tell us about your ideas.
Fill in the Form
Blockchain Development