05 Capítulo 5 · Lección 13

Interacciones 3D

Redactado por
Author Sebastian V.
Fecha de publicación

02/03/2026

Tiempo de lectura

8 min

Tema

Three.js

El clic en un mundo sin DOM

En HTML normal, hacer clic en un botón es fácil: añades un addEventListener. Pero en Three.js, toda tu escena es un único elemento HTML: el <canvas>. El navegador no tiene ni idea de si has hecho clic en un cubo 3D, una esfera, o en el vacío del espacio.

Raycaster: El francotirador invisible

Para detectar clics u hovers en objetos 3D, usamos un Raycaster. Funciona lanzando un rayo láser invisible matemáticamente exacto desde tu ratón (en la pantalla 2D), atravesando la lente de la cámara hacia las profundidades de la escena 3D.

El Raycaster devolverá una lista de TODOS los objetos que ese láser tocó en su camino, ordenados desde el más cercano hasta el más lejano.

Normalizando coordenadas

Tu ratón se mueve en píxeles (ej. 1920x1080), pero el mundo 3D y WebGL usan un sistema coordinado normalizado donde el centro de la pantalla es [0,0], arriba a la derecha es [1,1] y abajo a la izquierda es [-1,-1]. Deberás convertir la posición de tu ratón a este formato antes de disparar el rayo.

Consejo profesional

El Raycaster es costoso. Si tienes 1000 árboles, no lances el rayo contra todos en cada movimiento del ratón. Mete solo los objetos 'clickeables' en un Array y dile al Raycaster que solo compruebe las colisiones con esos objetos.

Detectando Hover en Objetos 3D

> _
// 1. Instanciamos el Raycaster y un vector para el ratón
const raycaster = new THREE.Raycaster();
const mouse = new THREE.Vector2();

// Array de cosas interactuables
const objectsToTest = [cubo1, cubo2, esfera];

// 2. Rastreamos el ratón y normalizamos coordenadas
window.addEventListener('mousemove', (event) => {
  // Fórmula estándar para convertir a escala -1 a +1
  mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
  mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
});

// 3. En el bucle de animación, disparamos el rayo
function animate() {
  requestAnimationFrame(animate);
  
  // Actualizar el rayo desde la cámara y la posición del ratón
  raycaster.setFromCamera(mouse, camera);
  
  // Obtener los objetos tocados
  const intersects = raycaster.intersectObjects(objectsToTest);
  
  // Restaurar el color de todos primero (pseudo :mouseout)
  for(const object of objectsToTest) {
    object.material.color.set('#ffffff');
  }
  
  // Si tocamos algo, cambiamos su color (pseudo :hover)
  if(intersects.length > 0) {
    intersects[0].object.material.color.set('#ff0000');
  }
  
  renderer.render(scene, camera);
}
> _

Pasa el ratón sobre los objetos. El Raycaster detectará la colisión matemáticamente.

Errores comunes

01
El hover está desfasado Si tu canvas no ocupa toda la pantalla (window.innerWidth), la fórmula de normalización fallará. Debes usar el clientWidth de tu contenedor y el offsetX del ratón.
02
La cámara cambió, el raycaster no Al usar setFromCamera(mouse, camera), si mueves la cámara con OrbitControls, el raycaster debe seguir actualizándose dentro del bucle animate, no fuera de él.

Practica lo aprendido

Dale tacto a tu mundo 3D.

01 Instancia new THREE.Raycaster() y un new THREE.Vector2().
02 En el mousemove, convierte las coordenadas del evento a -1 y 1.
03 En animate(), dispara raycaster.intersectObjects(tusMeshes) y cambia el color del primer elemento intersectado.