Skip to content

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

  1. Escena y Cámara:
  2. Se crea una escena con un fondo de color gris claro.
  3. Se configura una PerspectiveCamera con 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.
  4. La cámara se posiciona en (0, 5, 10) y mira hacia el centro de la escena.

  5. Renderizador:

  6. Se crea un WebGLRenderer con antialiasing habilitado para suavizar los bordes de los objetos.
  7. El renderizador se ajusta al tamaño de la ventana y se añade al documento HTML.

  8. OrbitControls:

  9. Se instancia OrbitControls pasando la cámara y el renderizador como parámetros.
  10. Se habilita la inercia (enableDamping) para que los movimientos de la cámara sean más suaves.
  11. 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.
  12. Se limita la rotación vertical para evitar que la cámara se coloque debajo del suelo.

  13. Iluminación:

  14. AmbientLight: Proporciona una iluminación base suave en toda la escena.
  15. 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.

  16. Objetos:

  17. Cubo: Un objeto verde con MeshStandardMaterial que responde a la iluminación. Está configurado para proyectar y recibir sombras.
  18. Suelo: Un plano gris que actúa como suelo, rotado para quedar horizontal y posicionado debajo del cubo. Está configurado para recibir sombras.

  19. Animación:

  20. Se define una función animate que se ejecuta en cada frame, actualizando los controles de la cámara y renderizando la escena.

  21. Redimensionamiento:

  22. 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

  1. Escena y Cámara:
  2. Se crea una escena con un fondo gris claro.
  3. Se configura una OrthographicCamera con 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.

  4. Renderizador:

  5. Se crea un WebGLRenderer con antialiasing habilitado para suavizar los bordes de los objetos.
  6. El renderizador se ajusta al tamaño de la ventana y se añade al documento HTML.

  7. OrbitControls:

  8. Se instancia OrbitControls pasando la cámara y el renderizador como parámetros.
  9. Se habilita la inercia (enableDamping) para movimientos suaves.
  10. Se establecen límites de zoom mínimo y máximo para controlar el acercamiento y alejamiento.
  11. Se habilitan todas las interacciones posibles: zoom, rotación y desplazamiento.

  12. Iluminación:

  13. AmbientLight: Proporciona una iluminación base suave en toda la escena.
  14. 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.

  15. Objetos:

  16. Esfera: Un objeto azul con MeshStandardMaterial que responde a la iluminación. Está configurado para proyectar y recibir sombras.
  17. Suelo: Un plano gris que actúa como suelo, rotado para quedar horizontal y posicionado debajo de la esfera. Está configurado para recibir sombras.

  18. Animación:

  19. Se define una función animate que se ejecuta en cada frame, actualizando los controles de la cámara y renderizando la escena.

  20. Redimensionamiento:

  21. 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

  1. Usar Helpers para Depuración:
  2. Emplea THREE.CameraHelper para visualizar el volumen de la cámara y asegurarte de que está configurada correctamente.
  3. 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);