Trying my hand at implementing shadows in Three.js, even though I am fairly new to JavaScript. My attempts to create a cube with a proper shadow have yielded partial results, as only a portion of the mesh seems to be casting the shadow.
https://i.sstatic.net/ON7XO.png
The expected shadow should resemble something like this: https://i.sstatic.net/LqbGq.png
I've configured two lights to cast shadows, along with a hemisphere light to enhance brightness. Despite referencing various tutorials and questions to set up the materials correctly, I am still struggling to achieve the desired outcome.
The shadows overall appear satisfactory, but they appear to only cover a portion of the object. Any direct guidance or links to detailed tutorials on this fundamental topic would be greatly appreciated. I even tried using an official example, but couldn't make it work after removing the object from their code. (Specifically this one: Link to official example
For more details, I am using an M1 MacBook Pro and encountering this issue on both Safari and Firefox.
Here is the script I am currently working with:
<!-- importmap added by TheJim01 -->
<script type="importmap">
{
"imports": {
"three": "https://threejs.org/build/three.module.js",
"three_orbitControls": "https://threejs.org/examples/jsm/controls/OrbitControls.js"
}
}
</script>
<script type="module">
import * as THREE from 'three';
import { Object3D } from 'three';
import { OrbitControls } from 'three_orbitControls'; // TheJim01 modified to use importmap
// Establish Scene
var scene = new THREE.Scene();
scene.background = new THREE.Color( 0xD3D3D3 );
scene.fog = new THREE.Fog( 0xa0a0a0, 10, 2000 );
// Add Ground
const ground = new THREE.Mesh(
new THREE.PlaneGeometry( 1000, 1000 ), // geometry
new THREE.MeshPhongMaterial( { //material
color: 0xFFFFFF,
depthWrite: false
} ) );
ground.rotation.x = - Math.PI / 2;
ground.receiveShadow = true;
scene.add( ground );
// Add directional lighting
const light = new THREE.DirectionalLight( "#FFFFFF", 1.25 );
light.position.set(-25, 50, 30);
light.castShadow = true; // default false
//Set up shadow properties for the light
light.shadow.mapSize.width = 512; // default
light.shadow.mapSize.height = 512; // default
light.shadow.camera.near = 0.1; // default
light.shadow.camera.far = 500; // default
scene.add( light );
const secondLight = new THREE.DirectionalLight ( "#FFFFFF", 0.5 );
secondLight.position.set(20, 30, -50);
secondLight.castShadow = true;
//Set up shadow properties for the light
secondLight.shadow.mapSize.width = 512; // default
secondLight.shadow.mapSize.height = 512; // default
secondLight.shadow.camera.near = 0.1; // default
secondLight.shadow.camera.far = 500; // default
scene.add( secondLight );
// Add hemisphere light
const hlight = new THREE.HemisphereLight( 0xffffbb, 0x080820, 1.5);
const helper = new THREE.HemisphereLightHelper( hlight, 1 );
//scene.add( helper );
//Set up shadow properties for the light
light.shadow.mapSize.width = 512; // default
light.shadow.mapSize.height = 512; // default
light.shadow.camera.near = 0.1; // default
light.shadow.camera.far = 500; // default
// Camera
const camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 );
const renderer = new THREE.WebGLRenderer({ antialias: true });
camera.position.z = 25;
camera.position.x = 25;
camera.position.y = 25;
camera.lookAt(0,0,0)
// Renderer
renderer.setSize( window.innerWidth , window.innerHeight );
renderer.setPixelRatio( window.devicePixelRatio );
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
renderer.shadowMapSoft = true;
renderer.shadowMapBias = 0.0039;
renderer.shadowMapWidth = 1024;
renderer.shadowMapHeight= 1024;
document.body.appendChild( renderer.domElement );
const controls = new OrbitControls( camera, renderer.domElement );
// Cube
const material = new THREE.MeshPhongMaterial({
color: 0xC8FAFF,
roughness: 10,
})
const geometry_1 = new THREE.BoxGeometry( 4, 5, 6 );
const cube1 = new THREE.Mesh( geometry_1, material );
cube1.position.set(
-5, // Width
0.5 * cube1.geometry.parameters.height, // Height
0); // Depth
cube1.castShadow = true;
cube1.receiveShadow = true;
scene.add( cube1 );
//animate the scene
function animate() {
requestAnimationFrame( animate );
controls.update();
renderer.render( scene, camera );
}
animate();
</script>