PROWAREtech
ThreeJS: Cast Shadows
Cast object shadows and receive shadows from objects; how to create shadows in THREE.js, including how to configure a 3D model to cast and receive a shadow.
These are the steps to create shadows in THREE.js:
- Create an HTML document with a
<CANVAS>
element. - Create a THREE.js scene object.
- Create a THREE.js camera object with the frustum defined; position the camera back a little to view the scene.
- Create a THREE.js renderer object.
- Set the
renderer.shadowMap.enabled
totrue
. - Create and add to the scene the THREE.js objects that will cast shadows on each other making sure to use a reflective material such as
MeshPhongMaterial
. - For each object set the
receiveShadow
andcastShadow
properties totrue
for a realistic rendering. - At least one light of a directional type must be created.
- Set its
castShadow
property totrue
. - Set its
shadow.mapSize.width
property to the canvas size or an arbitrary value (1024 works well). - Set its
shadow.mapSize.height
property to the canvas size or an arbitrary value (1024 works well). - Set its
shadow.camera.near
property to the camera's near frustum value. - Set its
shadow.camera.far
property to the camera's far frustum value. - Add the light to the scene.
- Set its
- Animate the scene with a standard animation function.
The following is an example of the above steps. It is layed out to show that shadows are cast and received by several scene objects.
<!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>Shadows Example</title>
<style>
body {
margin: 0;
padding: 0;
height: 100vh;
}
canvas {
display: block;
width: 100%;
height: 100vh;
}
</style>
</head>
<body>
<canvas></canvas>
<script src="/js/three.min.js"></script>
<script type="text/javascript">
(function () {
var canvas = document.getElementsByTagName("canvas")[0];
// NOTE: create the scene to place objects in
var scene = new THREE.Scene();
scene.background = new THREE.Color(0x6699CC); // NOTE: make the background blue for the sky
scene.matrixWorldAutoUpdate = true;
var size = {
width: canvas.offsetWidth,
height: canvas.offsetHeight
};
// NOTE: issue these statements when resizing the window
// camera.aspect = size.width / size.height;
// camera.updateProjectionMatrix();
// renderer.setPixelRatio(window.devicePixelRatio);
// renderer.setSize(size.width, size.height);
var cameraNear = 1, cameraFar = 5000;
// NOTE: create the camera with 60 degree field of view; this is how the scene is viewed by the user
var camera = new THREE.PerspectiveCamera(60, size.width / size.height, cameraNear, cameraFar);
// NOTE: position the camera in space a bit
camera.position.z = 20;
var renderer = new THREE.WebGLRenderer({
canvas: canvas,
antialias: true
});
renderer.shadowMap.enabled = true; // NOTE: must enable shadowing on the renderer
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(size.width, size.height);
renderer.render(scene, camera);
var objects = {};
objects.sphere = new THREE.Mesh(new THREE.SphereGeometry(2, 32, 32), new THREE.MeshPhongMaterial({ color: 0xFFFFFF, dithering: true }));
// NOTE: position each object in space
objects.sphere.position.x = -9;
objects.sphere.position.y = 3;
objects.sphere.position.z = -1;
// NOTE: receiving shadows and casting them is the most natural
objects.sphere.receiveShadow = true;
objects.sphere.castShadow = true;
scene.add(objects.sphere);
objects.box = new THREE.Mesh(new THREE.BoxGeometry(2, 2, 2), new THREE.MeshPhongMaterial({ color: 0xFFFFFF, dithering: true }));
objects.box.position.x = 0;
objects.box.position.y = 3.5;
objects.box.receiveShadow = true;
objects.box.castShadow = true;
scene.add(objects.box);
objects.torus = new THREE.Mesh(new THREE.TorusGeometry(1, 0.5, 16, 100), new THREE.MeshPhongMaterial({ color: 0xFFFFFF, dithering: true }));
objects.torus.position.x = 9;
objects.torus.position.y = 4;
objects.torus.position.z = 2;
objects.torus.receiveShadow = true;
objects.torus.castShadow = true;
scene.add(objects.torus);
// NOTE: create the ground plane and make sure it can receive shadows
var plane = new THREE.Mesh(new THREE.PlaneGeometry(1000, 1000), new THREE.MeshPhongMaterial({ color: 0x00aa00, dithering: true }));
plane.position.x = 0;
plane.position.y = 0;
plane.position.z = -50;
plane.rotation.x = 4.75;
plane.receiveShadow = true;
scene.add(plane);
var light = new THREE.PointLight(0xffffff, 1);
light.position.set(20, 8, 5); // NOTE: the farther away the light is from the objects then the blockier the shadows will be
// NOTE: must configure light to create shadows
light.castShadow = true;
light.shadow.mapSize.width = 1024;
light.shadow.mapSize.height = 1024;
light.shadow.camera.near = cameraNear;
light.shadow.camera.far = cameraFar;
scene.add(light);
scene.add(new THREE.AmbientLight(0xffffff, .33)); // NOTE: add a touch of ambient light
// NOTE: MUST HAVE AN ANIMATE FUNCTION
var animate = function () {
for(var name in objects) {
objects[name].rotation.x += 0.01;
objects[name].rotation.y += 0.02;
objects[name].rotation.z += 0.03;
}
renderer.render(scene, camera);
requestAnimationFrame(animate);
};
animate();
})();
</script>
</body>
</html>
Configure 3D Models to Cast and Receive Shadows
THREE.js supports 3D models and these can be configured to cast and receive shadows, too. Each node of the model must be configured to cast and/or receive a shadow.
// NOTE: load the GLTFLoader library first
var modelPath = "/gltf/model.gltf";
new THREE.GLTFLoader().load(modelPath, function (gltf) {
var model = gltf.scene;
model.traverse(function(node) {
if(node.isMesh) {
node.castShadow = node.receiveShadow = true; // NOTE: either property is optional
}
});
scene.add(model);
});
Comment