DirectionalLight parallel to the camera - step by step?

This is my first post.
So hello to all Three.js enthusiasts.
I was very intrigued by the possibility of presenting my 3D CAD models on websites.
I’m glad that I’ve learned a lot :), but I have a question about the directional light.
I can’t find a simple and complete example of how to implement a directional light parallel to the camera STEP BY STEP :sunglasses: in the project so that it works also when moving the camera (and at the same time the camera target).
Here is a simple example of a scene and moving the camera left/right (only along the X axis for simplicity). The light is sticked to the camera, but the target of the light does not move with the target of the camera and stays at 0,0,0:

is it possible to get a result like this (dynamic view, not static!):

sun.target.updateMatrixWorld();

ok but where in the script should i put it?
and maybe i should change something else?

EDIT: i added my whole script

import * as THREE from 'three';
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
import { RoomEnvironment } from 'three/addons/environments/RoomEnvironment.js';	
let camera, controls, scene, renderer;
let cam=[], ctar=[];

init();
animate();

function init() {

cam=[0, 55, 25];
ctar=[cam[0], 0, 5];


	scene = new THREE.Scene();
	scene.background = new THREE.Color( 0xffffff ); //0xd1d1d1

	renderer = new THREE.WebGLRenderer( { antialias: true } );
	renderer.setPixelRatio( window.devicePixelRatio );
	renderer.setSize( window.innerWidth, window.innerHeight );
	renderer.useLegacyLights = false;
	renderer.shadowMap.enabled = true;	
	renderer.toneMappingExposure = 1;
	renderer.outputEncoding = THREE.sRGBEncoding;
	document.body.appendChild( renderer.domElement );

	camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 1, 1000 );
	camera.position.set( cam[0], cam[1], cam[2] );

	// controls
	controls = new OrbitControls( camera, renderer.domElement );
	controls.listenToKeyEvents( window ); // optional
	controls.enableDamping = true;
	controls.dampingFactor = 0.05;
	controls.screenSpacePanning = false;
	controls.minDistance = 1;
	controls.maxDistance = 100;
	controls.target.set(ctar[0], ctar[1], ctar[2]);	// camera target	
	
	// lights
	const dirLight = new THREE.DirectionalLight( 0xffffff, 3 );
	dirLight.position.set(0, 10, 45);
	dirLight.shadow.mapSize.set( 4096, 4096 );
	dirLight.penumbra = 0.5;
	dirLight.shadow.bias = -0.0005;
	dirLight.shadow.camera.left =	-50;
	dirLight.shadow.camera.right = 	50;
	dirLight.shadow.camera.top = 	50;
	dirLight.shadow.camera.bottom = -50;
	dirLight.shadowCameraNear = 1;
	dirLight.shadowCameraFar = 500;
	dirLight.castShadow = true;

		camera.add( dirLight );
		
		scene.add( camera );
		let cameraHelper = new THREE.CameraHelper(camera);
		scene.add (cameraHelper);

	const ambientLight = new THREE.AmbientLight( 0xffffff, 1 );
	scene.add( ambientLight );

	///////////////// MODELs //////////////////////

	// PLANE
	const pgeo = new THREE.PlaneGeometry(40, 40);
	const pmat = new THREE.MeshPhongMaterial({ color: '0xf0f0f0' });
	const plane = new THREE.Mesh(pgeo, pmat);
	
	plane.material.side = THREE.DoubleSide;
	plane.rotateX(-Math.PI / 2);
	plane.position.set( 0,-0.003,0);
	plane.receiveShadow = true;
	scene.add( plane );

	// GRIDs
	const helper1 = new THREE.GridHelper( 20, 20, 'gray', 'gray' );
	helper1.position.y=-0.002;scene.add( helper1 );
	const helper2 = new THREE.GridHelper( 40, 8, 'silver', 'silver' );
	helper2.position.y=-0.001;scene.add( helper2 );
	
	// Axes
	const axesHelper = new THREE.AxesHelper( 25 );
	scene.add( axesHelper );

	// CYLINDERs
	const geometry = new THREE.CylinderGeometry( 0.5, 0.5, 10, 8 ); 
	const material = new THREE.MeshPhongMaterial( {color: 0xFFD966} );		
	for (let ii = 0; ii < 3; ii++) {		
	for (let i = 0; i < 3; i++) {
		const mesh = new THREE.Mesh( geometry, material );
			mesh.position.set( -10+i*10, 5 , -10+ii*10);
			mesh.receiveShadow = true;
			mesh.castShadow = true;	
		mesh.updateMatrix();
		mesh.matrixAutoUpdate = false;	
		scene.add( mesh );
	}}
	window.addEventListener( 'resize', onWindowResize );
}
function onWindowResize() {
	camera.aspect = window.innerWidth / window.innerHeight;
	camera.updateProjectionMatrix();
	renderer.setSize( window.innerWidth, window.innerHeight );
}
function animate() {
	requestAnimationFrame( animate );
	controls.update();
	render();
}
function render() {
	renderer.render( scene, camera );
}
function animate() {
	requestAnimationFrame( animate );
	controls.update();
	dirLight.target.position.copy(camera.position.);
	dirLight.target.position.y=0;
	dirLight.target.updateMatrixWorld();
	render();
}
1 Like

Black screen :frowning:

check your console for errors.

your dirLight is declared in your init scope.
Move it to the global scope.

let camera, controls, scene, renderer, dirLight;

and change
const dirLight = new THREE.DirectionalLight( 0xffffff, 3 );
to
dirLight = new THREE.DirectionalLight( 0xffffff, 3 );

1 Like

Chaser_Code and seanwasere YES YES YES ! :slight_smile:
Thank you for the quick reply. I implemented it successfully.
This allowed me to take another milestone in three.js :slight_smile:


Corrected script:

import * as THREE from 'three';

import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
import { RoomEnvironment } from 'three/addons/environments/RoomEnvironment.js';	

let camera, controls, scene, renderer, dirLight;
let cam=[], ctar=[];

init();
animate();

function init() {

cam=[0, 55, 25];
ctar=[cam[0], 0, 5];

	scene = new THREE.Scene();
	scene.background = new THREE.Color( 0xffffff ); //0xd1d1d1

	renderer = new THREE.WebGLRenderer( { antialias: true } );
	renderer.setPixelRatio( window.devicePixelRatio );
	renderer.setSize( window.innerWidth, window.innerHeight );
	renderer.useLegacyLights = false;
	renderer.shadowMap.enabled = true;	
	renderer.toneMappingExposure = 1;
	renderer.outputEncoding = THREE.sRGBEncoding;
	document.body.appendChild( renderer.domElement );

	camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 1, 1000 );
	camera.position.set( cam[0], cam[1], cam[2] );

	// controls
	controls = new OrbitControls( camera, renderer.domElement );
	controls.listenToKeyEvents( window ); // optional
	controls.enableDamping = true;
	controls.dampingFactor = 0.05;
	controls.screenSpacePanning = false;
	controls.minDistance = 1;
	controls.maxDistance = 100;
	controls.target.set(ctar[0], ctar[1], ctar[2]);	// camera target	
	
	// lights
	dirLight = new THREE.DirectionalLight( 0xffffff, 3 );
	dirLight.position.set(0, 1, 100);
	dirLight.shadow.mapSize.set( 4096, 4096 );
	dirLight.penumbra = 0.5;
	dirLight.shadow.bias = -0.0005;
	dirLight.shadow.camera.left =	-200;
	dirLight.shadow.camera.right = 	200;
	dirLight.shadow.camera.top = 	200;
	dirLight.shadow.camera.bottom = -200;
	dirLight.shadowCameraNear = 1;
	dirLight.shadowCameraFar = 500;
	dirLight.castShadow = true;
	
		camera.add( dirLight );
		
		scene.add( camera );
		let cameraHelper = new THREE.CameraHelper(camera);
		scene.add (cameraHelper);

	const ambientLight = new THREE.AmbientLight( 0xffffff, 1 );
	scene.add( ambientLight );

	///////////////// MODELs //////////////////////

	// PLANE
	const pgeo = new THREE.PlaneGeometry(40, 40);
	const pmat = new THREE.MeshPhongMaterial({ color: '0xf0f0f0' });
	const plane = new THREE.Mesh(pgeo, pmat);
	
	plane.material.side = THREE.DoubleSide;
	plane.rotateX(-Math.PI / 2);
	plane.position.set( 0,-0.003,0);
	plane.receiveShadow = true;
	scene.add( plane );

	// GRIDs
	const helper1 = new THREE.GridHelper( 20, 20, 'gray', 'gray' );
	helper1.position.y=-0.002;scene.add( helper1 );
	const helper2 = new THREE.GridHelper( 40, 8, 'silver', 'silver' );
	helper2.position.y=-0.001;scene.add( helper2 );
	
	// Axes
	const axesHelper = new THREE.AxesHelper( 25 );
	scene.add( axesHelper );

	// CYLINDERs
	const geometry = new THREE.CylinderGeometry( 0.5, 0.5, 10, 8 ); 
	const material = new THREE.MeshPhongMaterial( {color: 0xFFD966} );		
	for (let ii = 0; ii < 3; ii++) {		
	for (let i = 0; i < 3; i++) {
		const mesh = new THREE.Mesh( geometry, material );
			mesh.position.set( -10+i*10, 5 , -10+ii*10);
			mesh.receiveShadow = true;
			mesh.castShadow = true;	
		mesh.updateMatrix();
		mesh.matrixAutoUpdate = false;	
		scene.add( mesh );
	}}
	window.addEventListener( 'resize', onWindowResize );
}

function onWindowResize() {
	camera.aspect = window.innerWidth / window.innerHeight;
	camera.updateProjectionMatrix();
	renderer.setSize( window.innerWidth, window.innerHeight );
}
function animate() {
	requestAnimationFrame( animate );
	controls.update();
	// dirLight.target.position.copy(camera.position);
	dirLight.target.position.copy(controls.target);	// for me it works similarly
	// dirLight.target.position.y=0;
	dirLight.target.updateMatrixWorld();
	render();
}
function render() {
	renderer.render( scene, camera );
}
1 Like