5. Cámaras y Control de la Cámara
5.1 Introducción a las Cámaras
En Three.js, la cámara es el punto de vista desde el cual observas la escena 3D. Define qué parte de la escena es visible y cómo se proyecta en la pantalla. Seleccionar y configurar adecuadamente la cámara es esencial para lograr la composición deseada y una experiencia de usuario fluida.
5.2 Tipos de Cámaras en Three.js
Three.js ofrece principalmente dos tipos de cámaras: Cámara en Perspectiva (PerspectiveCamera) y Cámara Ortográfica (OrthographicCamera). Cada una tiene características y usos específicos.
5.2.1 Cámara en Perspectiva (PerspectiveCamera)
Descripción:
La PerspectiveCamera simula la percepción humana, donde los objetos más lejanos parecen más pequeños que los cercanos. Es ideal para escenas que requieren profundidad y realismo, como juegos, visualizaciones arquitectónicas y entornos inmersivos.
Sintaxis:
const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
Parámetros:
fov(Field of View): Campo de visión en grados. Define el ángulo de visión vertical.aspect: Relación de aspecto, generalmente el ancho de la ventana dividido por su altura.near: Plano cercano de recorte. Objetos más cercanos que esta distancia no se renderizan.far: Plano lejano de recorte. Objetos más alejados que esta distancia no se renderizan.
Ejemplo:
const camera = new THREE.PerspectiveCamera(
75, // Campo de visión de 75 grados
window.innerWidth / window.innerHeight, // Relación de aspecto
0.1, // Plano cercano
1000 // Plano lejano
);
camera.position.set(0, 5, 10); // Posicionar la cámara
camera.lookAt(0, 0, 0); // Orientar la cámara hacia el centro de la escena
5.2.2 Cámara Ortográfica (OrthographicCamera)
Descripción:
La OrthographicCamera muestra los objetos sin perspectiva, es decir, mantiene las mismas dimensiones independientemente de la distancia al observador. Es útil para aplicaciones donde la precisión y la escala son importantes, como diagramas técnicos, editores 2D o juegos de estilo isométrico.
Sintaxis:
const camera = new THREE.OrthographicCamera(left, right, top, bottom, near, far);
Parámetros:
left,right,top,bottom: Definen el tamaño del volumen de visualización ortográfica.near: Plano cercano de recorte.far: Plano lejano de recorte.
Ejemplo:
const aspect = window.innerWidth / window.innerHeight;
const camera = new THREE.OrthographicCamera(
-10 * aspect, // left
10 * aspect, // right
10, // top
-10, // bottom
0.1, // near
1000 // far
);
camera.position.set(0, 10, 10); // Posicionar la cámara
camera.lookAt(0, 0, 0); // Orientar la cámara hacia el centro de la escena
5.3 Configuración de Parámetros Importantes
5.3.1 Campo de Visión (fov)
El fov define el ángulo de visión vertical de la cámara en grados. Un valor mayor proporciona una visión más amplia, pero puede distorsionar la perspectiva, mientras que un valor menor enfoca más en un área específica.
Recomendación:
Un fov entre 60 y 75 grados suele ser adecuado para la mayoría de las aplicaciones, ya que proporciona una buena perspectiva sin exagerar la distorsión.
5.3.2 Relación de Aspecto (aspect)
La relación de aspecto es la proporción entre el ancho y la altura de la ventana de visualización. Es crucial para evitar distorsiones en la escena.
Ejemplo:
const aspect = window.innerWidth / window.innerHeight;
camera.aspect = aspect;
camera.updateProjectionMatrix();
5.3.3 Planos de Recorte (near y far)
-
near: Define la distancia mínima a la que la cámara empieza a renderizar objetos. Un valor muy cercano puede causar problemas de precisión, mientras que un valor muy lejano puede recortar objetos cercanos. -
far: Define la distancia máxima a la que la cámara deja de renderizar objetos. Debe ser lo suficientemente grande para incluir todos los objetos de interés, pero no demasiado grande para evitar problemas de precisión.
Recomendación:
Mantén el rango entre near y far lo más estrecho posible para optimizar la profundidad de la escena y mejorar el rendimiento.
5.4 Controles de la Cámara
Para mejorar la experiencia de usuario, es común implementar controles que permitan interactuar con la cámara, como rotar, acercar o alejar la vista. Three.js proporciona OrbitControls para este propósito.
5.4.1 OrbitControls
Descripción:
OrbitControls permite rotar, acercar y alejar la cámara alrededor de un punto objetivo. Es ideal para aplicaciones donde el usuario necesita explorar la escena desde diferentes ángulos.
Instalación:
Si estás utilizando un CDN, puedes incluir OrbitControls directamente desde el repositorio de Three.js.
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r152/three.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r152/examples/js/controls/OrbitControls.js"></script>
Si estás utilizando módulos ES6 con import, asegúrate de importar correctamente OrbitControls.
Uso Básico:
// Crear los controles después de haber creado la cámara y el renderizador
const controls = new THREE.OrbitControls(camera, renderer.domElement);
// Configurar opciones de los controles
controls.enableDamping = true; // Habilita la inercia
controls.dampingFactor = 0.05; // Factor de inercia
controls.screenSpacePanning = false; // Deshabilita el desplazamiento en el espacio de la pantalla
controls.minDistance = 5; // Distancia mínima al objeto
controls.maxDistance = 50; // Distancia máxima al objeto
controls.maxPolarAngle = Math.PI / 2; // Limita la rotación vertical a 90 grados
Actualización en el Ciclo de Animación:
Es importante actualizar los controles en cada frame para que las animaciones de inercia funcionen correctamente.
function animate() {
requestAnimationFrame(animate);
controls.update(); // Actualiza los controles
renderer.render(scene, camera);
}
animate();
Opciones Avanzadas:
-
controls.target: Define el punto alrededor del cual la cámara orbita. Por defecto, es(0, 0, 0).javascript controls.target.set(0, 0, 0); -
controls.enableZoom: Habilita o deshabilita el zoom.javascript controls.enableZoom = true; -
controls.enableRotate: Habilita o deshabilita la rotación.javascript controls.enableRotate = true; -
controls.enablePan: Habilita o deshabilita el desplazamiento lateral.javascript controls.enablePan = true;
5.5 Ejemplo Completo: Cámara en Perspectiva con OrbitControls
A continuación, se presenta un ejemplo completo que integra una PerspectiveCamera con OrbitControls, permitiendo al usuario interactuar con la cámara para explorar la escena.
5.5.1 Estructura de Carpetas
mi-escena-camara-threejs/
├── index.html
├── js/
│ └── three.min.js
└── examples/
└── OrbitControls.js
5.5.2 Contenido de index.html
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<title>Cámara y Controles en Three.js</title>
<style>
body { margin: 0; }
canvas { display: block; }
</style>
</head>
<body>
<!-- Enlace a Three.js -->
<script src="js/three.min.js"></script>
<!-- Enlace a OrbitControls -->
<script src="examples/OrbitControls.js"></script>
<script>
// 1. Crear la escena
const scene = new THREE.Scene();
scene.background = new THREE.Color(0xdddddd);
// 2. Crear la cámara en perspectiva
const camera = new THREE.PerspectiveCamera(
75, // Campo de visión
window.innerWidth / window.innerHeight, // Relación de aspecto
0.1, // Plano cercano
1000 // Plano lejano
);
camera.position.set(0, 5, 10); // Posicionar la cámara
camera.lookAt(0, 0, 0); // Orientar la cámara hacia el centro de la escena
// 3. Crear el renderizador
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
// 4. Añadir OrbitControls
const controls = new THREE.OrbitControls(camera, renderer.domElement);
controls.enableDamping = true; // Habilitar inercia
controls.dampingFactor = 0.05; // Factor de inercia
controls.minDistance = 5; // Distancia mínima
controls.maxDistance = 50; // Distancia máxima
controls.maxPolarAngle = Math.PI / 2; // Limitar rotación vertical
// 5. Añadir una luz ambiental
const ambientLight = new THREE.AmbientLight(0x404040, 1); // Luz ambiental suave
scene.add(ambientLight);
// 6. Añadir una luz direccional
const directionalLight = new THREE.DirectionalLight(0xffffff, 1);
directionalLight.position.set(5, 10, 7.5);
directionalLight.castShadow = true; // La luz proyecta sombras
// Configuración de sombras para la luz direccional
directionalLight.shadow.mapSize.width = 1024;
directionalLight.shadow.mapSize.height = 1024;
directionalLight.shadow.camera.near = 0.5;
directionalLight.shadow.camera.far = 50;
directionalLight.shadow.camera.left = -10;
directionalLight.shadow.camera.right = 10;
directionalLight.shadow.camera.top = 10;
directionalLight.shadow.camera.bottom = -10;
scene.add(directionalLight);
// 7. Crear un cubo que proyecta y recibe sombras
const geometry = new THREE.BoxGeometry(2, 2, 2);
const material = new THREE.MeshStandardMaterial({ color: 0x00ff00 });
const cube = new THREE.Mesh(geometry, material);
cube.castShadow = true; // El cubo proyecta sombras
cube.receiveShadow = true; // El cubo puede recibir sombras
scene.add(cube);
// 8. Crear el suelo que recibe sombras
const floorGeometry = new THREE.PlaneGeometry(20, 20);
const floorMaterial = new THREE.MeshStandardMaterial({ color: 0x808080 });
const floor = new THREE.Mesh(floorGeometry, floorMaterial);
floor.rotation.x = -Math.PI / 2; // Rotar para que quede horizontal
floor.position.y = -1;
floor.receiveShadow = true; // El suelo recibe sombras
scene.add(floor);
// 9. Función de animación
function animate() {
requestAnimationFrame(animate);
controls.update(); // Actualizar los controles
renderer.render(scene, camera);
}
animate();
// 10. Manejar el redimensionamiento de la ventana
window.addEventListener('resize', () => {
const width = window.innerWidth;
const height = window.innerHeight;
renderer.setSize(width, height);
camera.aspect = width / height;
camera.updateProjectionMatrix();
});
</script>
</body>
</html>
5.5.3 Explicación del Código
- Escena y Cámara:
- Se crea una escena con un fondo de color gris claro.
- Se configura una
PerspectiveCameracon un campo de visión de 75 grados, una relación de aspecto basada en el tamaño de la ventana y planos de recorte cercanos y lejanos de 0.1 y 1000, respectivamente. -
La cámara se posiciona en
(0, 5, 10)y mira hacia el centro de la escena. -
Renderizador:
- Se crea un
WebGLRenderercon antialiasing habilitado para suavizar los bordes de los objetos. -
El renderizador se ajusta al tamaño de la ventana y se añade al documento HTML.
-
OrbitControls:
- Se instancia
OrbitControlspasando la cámara y el renderizador como parámetros. - Se habilita la inercia (
enableDamping) para que los movimientos de la cámara sean más suaves. - Se establecen límites de distancia mínima y máxima para evitar acercamientos excesivos o alejamientos que puedan comprometer la visibilidad de la escena.
-
Se limita la rotación vertical para evitar que la cámara se coloque debajo del suelo.
-
Iluminación:
- AmbientLight: Proporciona una iluminación base suave en toda la escena.
-
DirectionalLight: Simula una luz direccional (como el sol), posicionada en
(5, 10, 7.5)y configurada para proyectar sombras. Se ajustan sus propiedades de sombra para mejorar la calidad visual. -
Objetos:
- Cubo: Un objeto verde con
MeshStandardMaterialque responde a la iluminación. Está configurado para proyectar y recibir sombras. -
Suelo: Un plano gris que actúa como suelo, rotado para quedar horizontal y posicionado debajo del cubo. Está configurado para recibir sombras.
-
Animación:
-
Se define una función
animateque se ejecuta en cada frame, actualizando los controles de la cámara y renderizando la escena. -
Redimensionamiento:
- Se añade un evento que ajusta el tamaño del renderizador y la cámara cuando la ventana cambia de tamaño, manteniendo la proporción y la calidad visual.
5.6 Ejemplo Completo: Cámara Ortográfica con Controles Personalizados
Para contrastar con el ejemplo anterior, a continuación se presenta un ejemplo que utiliza una OrthographicCamera junto con OrbitControls, permitiendo una visualización sin perspectiva de la escena.
5.6.1 Estructura de Carpetas
mi-escena-ortografica-threejs/
├── index.html
├── js/
│ └── three.min.js
└── examples/
└── OrbitControls.js
5.6.2 Contenido de index.html
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<title>Cámara Ortográfica en Three.js</title>
<style>
body { margin: 0; }
canvas { display: block; }
</style>
</head>
<body>
<!-- Enlace a Three.js -->
<script src="js/three.min.js"></script>
<!-- Enlace a OrbitControls -->
<script src="examples/OrbitControls.js"></script>
<script>
// 1. Crear la escena
const scene = new THREE.Scene();
scene.background = new THREE.Color(0xeeeeee);
// 2. Crear la cámara ortográfica
const aspect = window.innerWidth / window.innerHeight;
const camera = new THREE.OrthographicCamera(
-10 * aspect, // left
10 * aspect, // right
10, // top
-10, // bottom
0.1, // near
1000 // far
);
camera.position.set(20, 20, 20); // Posicionar la cámara
camera.lookAt(0, 0, 0); // Orientar la cámara hacia el centro de la escena
// 3. Crear el renderizador
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
// 4. Añadir OrbitControls
const controls = new THREE.OrbitControls(camera, renderer.domElement);
controls.enableDamping = true; // Habilitar inercia
controls.dampingFactor = 0.05; // Factor de inercia
controls.enableZoom = true; // Habilitar zoom
controls.enableRotate = true; // Habilitar rotación
controls.enablePan = true; // Habilitar desplazamiento
controls.minZoom = 0.5; // Zoom mínimo
controls.maxZoom = 2; // Zoom máximo
// 5. Añadir una luz ambiental
const ambientLight = new THREE.AmbientLight(0x404040, 1); // Luz ambiental suave
scene.add(ambientLight);
// 6. Añadir una luz direccional
const directionalLight = new THREE.DirectionalLight(0xffffff, 1);
directionalLight.position.set(10, 20, 10);
directionalLight.castShadow = true; // La luz proyecta sombras
// Configuración de sombras para la luz direccional
directionalLight.shadow.mapSize.width = 1024;
directionalLight.shadow.mapSize.height = 1024;
directionalLight.shadow.camera.near = 0.5;
directionalLight.shadow.camera.far = 50;
directionalLight.shadow.camera.left = -15;
directionalLight.shadow.camera.right = 15;
directionalLight.shadow.camera.top = 15;
directionalLight.shadow.camera.bottom = -15;
scene.add(directionalLight);
// 7. Crear una esfera que proyecta y recibe sombras
const geometry = new THREE.SphereGeometry(3, 32, 32);
const material = new THREE.MeshStandardMaterial({ color: 0x0077ff });
const sphere = new THREE.Mesh(geometry, material);
sphere.castShadow = true; // La esfera proyecta sombras
sphere.receiveShadow = true; // La esfera puede recibir sombras
scene.add(sphere);
// 8. Crear el suelo que recibe sombras
const floorGeometry = new THREE.PlaneGeometry(50, 50);
const floorMaterial = new THREE.MeshStandardMaterial({ color: 0x888888 });
const floor = new THREE.Mesh(floorGeometry, floorMaterial);
floor.rotation.x = -Math.PI / 2; // Rotar para que quede horizontal
floor.position.y = -5;
floor.receiveShadow = true; // El suelo recibe sombras
scene.add(floor);
// 9. Función de animación
function animate() {
requestAnimationFrame(animate);
controls.update(); // Actualizar los controles
renderer.render(scene, camera);
}
animate();
// 10. Manejar el redimensionamiento de la ventana
window.addEventListener('resize', () => {
const width = window.innerWidth;
const height = window.innerHeight;
// Actualizar la relación de aspecto y los parámetros de la cámara ortográfica
const aspect = width / height;
camera.left = -10 * aspect;
camera.right = 10 * aspect;
camera.top = 10;
camera.bottom = -10;
camera.updateProjectionMatrix();
renderer.setSize(width, height);
});
</script>
</body>
</html>
5.6.3 Explicación del Código
- Escena y Cámara:
- Se crea una escena con un fondo gris claro.
-
Se configura una
OrthographicCameracon un volumen de visualización que depende de la relación de aspecto de la ventana. La cámara se posiciona en(20, 20, 20)y mira hacia el centro de la escena. -
Renderizador:
- Se crea un
WebGLRenderercon antialiasing habilitado para suavizar los bordes de los objetos. -
El renderizador se ajusta al tamaño de la ventana y se añade al documento HTML.
-
OrbitControls:
- Se instancia
OrbitControlspasando la cámara y el renderizador como parámetros. - Se habilita la inercia (
enableDamping) para movimientos suaves. - Se establecen límites de zoom mínimo y máximo para controlar el acercamiento y alejamiento.
-
Se habilitan todas las interacciones posibles: zoom, rotación y desplazamiento.
-
Iluminación:
- AmbientLight: Proporciona una iluminación base suave en toda la escena.
-
DirectionalLight: Simula una luz direccional (como el sol), posicionada en
(10, 20, 10)y configurada para proyectar sombras. Se ajustan sus propiedades de sombra para mejorar la calidad visual. -
Objetos:
- Esfera: Un objeto azul con
MeshStandardMaterialque responde a la iluminación. Está configurado para proyectar y recibir sombras. -
Suelo: Un plano gris que actúa como suelo, rotado para quedar horizontal y posicionado debajo de la esfera. Está configurado para recibir sombras.
-
Animación:
-
Se define una función
animateque se ejecuta en cada frame, actualizando los controles de la cámara y renderizando la escena. -
Redimensionamiento:
- Se añade un evento que ajusta el tamaño del renderizador y actualiza los parámetros de la cámara ortográfica cuando la ventana cambia de tamaño, manteniendo la proporción y evitando distorsiones.
5.7 Mejores Prácticas
- Usar Helpers para Depuración:
- Emplea
THREE.CameraHelperpara visualizar el volumen de la cámara y asegurarte de que está configurada correctamente. - Los helpers facilitan la depuración y el ajuste fino de la configuración de la cámara.
Ejemplo de Uso de CameraHelper:
// Crear un helper para la cámara direccional
const directionalLightHelper = new THREE.DirectionalLightHelper(directionalLight, 5);
scene.add(directionalLightHelper);
// Crear un helper para la cámara
const cameraHelper = new THREE.CameraHelper(camera);
scene.add(cameraHelper);