How to move the shadowMap as camera moves?

This is my code:

Link on http://xw2.rf.gd/ to view site and link view-source:http://xw2.rf.gd/?i=1 to view source code.

My most important file is this:
script.js (9.1 KB)

And… the shadow map generation is good, but limited in a region of 100 blocks or 1000 blocks, i dont remember… so… the shadow map dissapear when i go far with the camera, so… it’s possible to move the shadow map with the camera?

1 Like

You do that by moving the DirectionalLight, yeah?

Moving the light is tricky too because the light has a .target position that has to be updated as well…

otherwise the light changes direction if you only change light.position
so when you move the light… you have to move light.position and light.target.position by the same amount.

And… if you don’t have scene.add(light.target) you may have to update the targets matrix as well manually…
light.target.updateMatrix()

I tried to change the light position, but it keeps as equal.

Is your light .add to the scene? You may have to scene.add(light) and scene.add(light.target)

…or light.updateMatrix() + light.updateMatrixWorld()
…or light.target.updateMatrix() + light.target.updateMatrixWorld()

not sure… it’s been a while… but I do remember its a bit tricky…

Also… when moving the light… it can make the shadows shimmer as they move… so a solution there is to only reposition the light after the target has moved a certain distance… so it only shimmers once every couple seconds as the player is moving through the scene.

1 Like

This is the code:

// Función para actualizar la posición del personaje según las teclas presionadas
function updatePosition() {
light.position.set(20+camera.position.x, 40, 20+camera.position.z);
const speed = 1; // Velocidad de movimiento del personaje
if (moveForward) {
controls.getObject().position.x -= speed * Math.sin(getCameraRotationY());
controls.getObject().position.z -= speed * Math.cos(getCameraRotationY());
}
if (moveBackward) {
controls.getObject().position.x += speed * Math.sin(getCameraRotationY());
controls.getObject().position.z += speed * Math.cos(getCameraRotationY());
}
if (moveLeft) {
controls.getObject().position.x -= speed * Math.cos(getCameraRotationY());
controls.getObject().position.z += speed * Math.sin(getCameraRotationY());
}
if (moveRight) {
controls.getObject().position.x += speed * Math.cos(getCameraRotationY());
controls.getObject().position.z -= speed * Math.sin(getCameraRotationY());
}
if (moveUp) {
controls.getObject().position.y += speed;
}
if (moveDown) {
controls.getObject().position.y -= speed;
}
}

Yeah… re-read my posts… I edited them some :smiley:

It appears as it when i move the player:


The code is this:
let scene, camera, renderer, controls;
let moveForward = false;
let moveBackward = false;
let moveLeft = false;
let moveRight = false;
let moveUp = false;
let moveDown = false;

function init() {
// Escena
scene = new THREE.Scene();

// Cámara
camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.set(5, 5, 5);

// Renderizador con antialiasing y sombras habilitadas
renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap; // Tipo de sombra suave
renderer.toneMapping = THREE.CineonToneMapping ;
document.body.appendChild(renderer.domElement);

// Definir un color de fondo de la escena
const backgroundColor = new THREE.Color("#08f");
scene.background = backgroundColor;

// Configurar luz direccional simulando la luz del día
const light = new THREE.DirectionalLight("#ff8", 3); // Color amarillo, intensidad 3
light.position.set(20, 40, 20); // Posición de la luz
light.castShadow = true; // Permitir que la luz emita sombras

// Ajustes de sombra
light.shadow.mapSize.width = 4096; // Ancho del mapa de sombra
light.shadow.mapSize.height = 4096; // Alto del mapa de sombra
light.shadow.camera.near = 0.5; // Distancia cercana de la cámara de sombra
light.shadow.camera.far = 1000; // Distancia lejana de la cámara de sombra
light.shadow.camera.left = -200; // Margen izquierdo de la cámara de sombra
light.shadow.camera.right = 200; // Margen derecho de la cámara de sombra
light.shadow.camera.top = 200; // Margen superior de la cámara de sombra
light.shadow.camera.bottom = -200; // Margen inferior de la cámara de sombra

scene.add(light);

// Crear un InstancedMesh para los cubos
const worldSize = 50; // Tamaño del mundo
const cubeSize = 1; // Tamaño de cada cubo
const depth = 15; // Profundidad del terreno

const instancesCount = (worldSize * 2 + 1) * (worldSize * 2 + 1); // Número total de instancias de superficie
const dirtInstancesCount = instancesCount * depth; // Número total de instancias de subsuelo

const cubeGeometry = new THREE.BoxGeometry(cubeSize, cubeSize, cubeSize);
const textureLoader = new THREE.TextureLoader();

// Cargar texturas (ajusta las rutas según tus texturas)
const cubeTextureTop = textureLoader.load('top.jpg');
const cubeTextureSide = textureLoader.load('side.jpg');
const cubeTextureBottom = textureLoader.load('bottom.jpg');

// Crear materiales para cada cara del cubo
const materials = [
    new THREE.MeshStandardMaterial({ map: cubeTextureSide }),   // Cara frontal
    new THREE.MeshStandardMaterial({ map: cubeTextureSide }),   // Cara trasera
    new THREE.MeshStandardMaterial({ map: cubeTextureTop }),    // Cara superior
    new THREE.MeshStandardMaterial({ map: cubeTextureBottom }), // Cara inferior
    new THREE.MeshStandardMaterial({ map: cubeTextureSide }),   // Cara derecha
    new THREE.MeshStandardMaterial({ map: cubeTextureSide })    // Cara izquierda
];

const dirtMaterials = [
    new THREE.MeshStandardMaterial({ map: cubeTextureBottom }), // Cara frontal
    new THREE.MeshStandardMaterial({ map: cubeTextureBottom }), // Cara trasera
    new THREE.MeshStandardMaterial({ map: cubeTextureBottom }), // Cara superior
    new THREE.MeshStandardMaterial({ map: cubeTextureBottom }), // Cara inferior
    new THREE.MeshStandardMaterial({ map: cubeTextureBottom }), // Cara derecha
    new THREE.MeshStandardMaterial({ map: cubeTextureBottom })  // Cara izquierda
];

// Crear los InstancedMeshes
const instancedMesh = new THREE.InstancedMesh(cubeGeometry, materials, instancesCount);
instancedMesh.castShadow = true;
instancedMesh.receiveShadow = true;


const dirtInstancedMesh = new THREE.InstancedMesh(cubeGeometry, dirtMaterials, dirtInstancesCount);
dirtInstancedMesh.castShadow = true;
dirtInstancedMesh.receiveShadow = true;


generateWorld=(x1,z1)=>{
    scene.add(instancedMesh);
    scene.add(dirtInstancedMesh);
    
    // Llenar los InstancedMeshes con las posiciones de los cubos
    const matrix = new THREE.Matrix4();
    let index = 0;
    for (let x = x1-worldSize; x <= x1+worldSize; x++) {
        for (let z = z1-worldSize; z <= z1+worldSize; z++) {
            const surface = Math.floor(
                noise.simplex2(x / 35, z / 35) * 6 +
                noise.simplex2(x / 150, z / 150) * 30
            ); // Altura del terreno

            matrix.makeTranslation(x, surface, z);
            instancedMesh.setMatrixAt(index, matrix);

            for (let i = 1; i < depth; i++) {
                matrix.makeTranslation(x, surface - i , z);
                dirtInstancedMesh.setMatrixAt(index * depth + i, matrix);
            }

            index++;
        }
    }
    instancedMesh.instanceMatrix.needsUpdate = true;
    dirtInstancedMesh.instanceMatrix.needsUpdate = true;
}

removeWorld=()=>{
	scene.remove(instancedMesh)
	scene.remove(dirtInstancedMesh)
}

generateWorld(0,0)

setInterval(()=>{
	removeWorld()
	generateWorld(Math.floor(camera.position.x),Math.floor(camera.position.z))
},500)

// Añadir PointerLockControls (opcional para modo espectador)
controls = new THREE.PointerLockControls(camera, document.body);
document.addEventListener('click', () => {
    controls.lock();
});
scene.add(controls.getObject()); // Agregar la cámara al mundo

// Manejar el redimensionamiento de la ventana
window.addEventListener('resize', onWindowResize);

// Manejar eventos de teclado para control de movimiento
document.addEventListener('keydown', onKeyDown);
document.addEventListener('keyup', onKeyUp);

// Llamar a la función de renderizado
render();

}

// Función para manejar el redimensionamiento de la ventana
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
render();
}

// Función para manejar eventos de teclado (presionar tecla)
function onKeyDown(event) {
switch (event.code) {
case ‘KeyW’:
moveForward = true;
break;
case ‘KeyS’:
moveBackward = true;
break;
case ‘KeyA’:
moveLeft = true;
break;
case ‘KeyD’:
moveRight = true;
break;
case ‘KeyQ’:
moveDown = true;
break;
case ‘KeyE’:
moveUp = true;
break;
}
}

// Función para manejar eventos de teclado (soltar tecla)
function onKeyUp(event) {
switch (event.code) {
case ‘KeyW’:
moveForward = false;
break;
case ‘KeyS’:
moveBackward = false;
break;
case ‘KeyA’:
moveLeft = false;
break;
case ‘KeyD’:
moveRight = false;
break;
case ‘KeyQ’:
moveDown = false;
break;
case ‘KeyE’:
moveUp = false;
break;
}
}

// Función para actualizar la posición del personaje según las teclas presionadas
function updatePosition() {
light.position.set(20+camera.position.x, 40, 20+camera.position.z);
light.updateMatrix() + light.updateMatrixWorld()
light.target.updateMatrix() + light.target.updateMatrixWorld()
const speed = 1; // Velocidad de movimiento del personaje
if (moveForward) {
controls.getObject().position.x -= speed * Math.sin(getCameraRotationY());
controls.getObject().position.z -= speed * Math.cos(getCameraRotationY());
}
if (moveBackward) {
controls.getObject().position.x += speed * Math.sin(getCameraRotationY());
controls.getObject().position.z += speed * Math.cos(getCameraRotationY());
}
if (moveLeft) {
controls.getObject().position.x -= speed * Math.cos(getCameraRotationY());
controls.getObject().position.z += speed * Math.sin(getCameraRotationY());
}
if (moveRight) {
controls.getObject().position.x += speed * Math.cos(getCameraRotationY());
controls.getObject().position.z -= speed * Math.sin(getCameraRotationY());
}
if (moveUp) {
controls.getObject().position.y += speed;
}
if (moveDown) {
controls.getObject().position.y -= speed;
}
}

// Función para obtener la rotación de la cámara en el eje Y
function getCameraRotationY() {
const euler = new THREE.Euler();
euler.setFromQuaternion(camera.quaternion, ‘YXZ’);
return euler.y;
}

// Función para renderizar la escena
function render() {
updatePosition(); // Actualizar posición según teclas presionadas
renderer.render(scene, camera);
requestAnimationFrame(render);
}

// Llamar a la función de inicialización al cargar la página
window.onload = init;

Yea… re-read what I said about light.target

https://threejs.org/docs/#api/en/lights/DirectionalLight

https://threejs.org/docs/#api/en/lights/DirectionalLight.target

Its correct?:

targetObject = new THREE.Object3D(); 
scene.add(targetObject);

setInterval(()=>{
    light.target = targetObject;
    light.position.set(20+camera.position.x, 40+camera.position.y, 20+camera.position.z);
    targetObject.position.set(camera.position.x,camera.position.y,camera.position.z);
    light.updateMatrix() + light.updateMatrixWorld()
    light.target.updateMatrix() + light.target.updateMatrixWorld()
    
	removeWorld()
	generateWorld(Math.floor(camera.position.x),Math.floor(camera.position.z))
},500)

Sorry my ignorance…

I also tried this:

targetObject = new THREE.Object3D(); 
scene.add(targetObject);

generateWorld(0,0)

setInterval(()=>{
    light.target = targetObject;
    light.position.set(20, 40, 20);
    targetObject.position.set(0,0,0);
    light.updateMatrix() + light.updateMatrixWorld()
    light.target.updateMatrix() + light.target.updateMatrixWorld()
    
	removeWorld()
	generateWorld(Math.floor(camera.position.x),Math.floor(camera.position.z))
},500)

And still generating terrain without the shadow.

hehe sorry I didn’t mean + there… just call both:

light.target.updateMatrix()
light.target.updateMatrixWorld()
Are there any errors in your console?

No, this is the state, when i walk far away the player respawn origin:

Remember is a screenshot taked behind the world, looking at the top…

This is my code:

targetObject = new THREE.Object3D(); 
scene.add(targetObject);
light.target = targetObject;
scene.add(light.target);

generateWorld(0,0)

setInterval(()=>{
	light.target.updateMatrix()
	light.target.updateMatrixWorld()
	removeWorld()
	generateWorld(Math.floor(camera.position.x),Math.floor(camera.position.z))
},500)

Hmm yeah hard to say. Can you put things into a jsfiddle or something? Maybe we can look and figure it out…

I dont know how…

But im going to try it…

Im trying and i dont know how to import the external scripts…

There are here:

three.min.js (604.4 KB)
perlin.js (10.2 KB)
PointerLockControls.js (3.1 KB)
style.css (172 Bytes)

And the main file:

script.js (9.3 KB)

And there are the textures:



And the html file:

index.html (734 Bytes)

Uff… yeah maybe someone else will help you with this.

I’m trying to avoid downloading peoples projects to help them, because it’s too much like actual work. :smiley: I hope you understand.

Yeah, i only want to add shadows like drgreenheck’s ‘minecraft’, but its a hard way i must solve using chatgpt, like in the past.

1 Like