Skip to content

3. Geometrías y Materiales

3.1 Introducción a las Geometrías y Materiales

En Three.js, las geometrías definen la forma de los objetos 3D, mientras que los materiales determinan cómo se ven esas formas, incluyendo su color, textura, reflejos y otros efectos visuales. La combinación de geometrías y materiales te permite crear una amplia variedad de objetos con diferentes apariencias y comportamientos en tu escena.

3.2 Geometrías Básicas

Three.js ofrece una variedad de geometrías predefinidas que puedes utilizar para crear objetos 3D comunes. A continuación, se describen algunas de las geometrías más utilizadas:

3.2.1 BoxGeometry (Caja)

Crea una caja o cubo con dimensiones especificadas.

Sintaxis:

const geometry = new THREE.BoxGeometry(ancho, alto, profundidad, segmentosAncho, segmentosAlto, segmentosProfundidad);

Ejemplo:

const geometry = new THREE.BoxGeometry(1, 1, 1); // Caja de 1 unidad en cada dimensión

3.2.2 SphereGeometry (Esfera)

Crea una esfera con radio y segmentos especificados.

Sintaxis:

const geometry = new THREE.SphereGeometry(radio, segmentosAncho, segmentosAlto);

Ejemplo:

const geometry = new THREE.SphereGeometry(1, 32, 32); // Esfera con radio 1 y 32 segmentos

3.2.3 PlaneGeometry (Plano)

Crea un plano bidimensional.

Sintaxis:

const geometry = new THREE.PlaneGeometry(ancho, alto, segmentosAncho, segmentosAlto);

Ejemplo:

const geometry = new THREE.PlaneGeometry(5, 5); // Plano de 5x5 unidades

3.2.4 CylinderGeometry (Cilindro)

Crea un cilindro con radio superior e inferior, altura y segmentos especificados.

Sintaxis:

const geometry = new THREE.CylinderGeometry(radioSuperior, radioInferior, altura, segmentosRadiales, segmentosAltura);

Ejemplo:

const geometry = new THREE.CylinderGeometry(1, 1, 2, 32); // Cilindro con radio 1, altura 2 y 32 segmentos radiales

3.2.5 TorusGeometry (Toro)

Crea un toro (donut) con radio mayor y menor, y segmentos especificados.

Sintaxis:

const geometry = new THREE.TorusGeometry(radioMayor, radioMenor, segmentosRadiales, segmentosTubulares);

Ejemplo:

const geometry = new THREE.TorusGeometry(1, 0.4, 16, 100); // Toro con radio mayor 1, radio menor 0.4, 16 segmentos radiales y 100 tubulares

3.2.6 Geometrías Personalizadas

Además de las geometrías predefinidas, puedes crear geometrías personalizadas utilizando THREE.BufferGeometry y definiendo tus propios vértices y caras. Esto es útil para objetos más complejos o específicos que no se pueden lograr con las geometrías básicas.

Ejemplo de creación de una geometría personalizada:

const vertices = new Float32Array([
    -1.0, -1.0,  1.0,
     1.0, -1.0,  1.0,
     1.0,  1.0,  1.0,
    -1.0,  1.0,  1.0,
    -1.0, -1.0, -1.0,
     1.0, -1.0, -1.0,
     1.0,  1.0, -1.0,
    -1.0,  1.0, -1.0,
]);

const indices = [
    0, 1, 2,  0, 2, 3, // Front face
    4, 5, 6,  4, 6, 7, // Back face
    0, 4, 7,  0, 7, 3, // Left face
    1, 5, 6,  1, 6, 2, // Right face
    3, 2, 6,  3, 6, 7, // Top face
    0, 1, 5,  0, 5, 4  // Bottom face
];

const geometry = new THREE.BufferGeometry();
geometry.setAttribute('position', new THREE.BufferAttribute(vertices, 3));
geometry.setIndex(indices);
geometry.computeVertexNormals();

3.3 Materiales Básicos

Three.js proporciona una variedad de materiales que puedes aplicar a tus geometrías para definir su apariencia. A continuación, se describen algunos de los materiales más comunes:

3.3.1 MeshBasicMaterial

Un material básico que no responde a la iluminación. Es útil para objetos que deben mantenerse siempre visibles con un color constante.

Sintaxis:

const material = new THREE.MeshBasicMaterial({ color: 0xff0000 });

Propiedades comunes:

  • color: Color del material.
  • wireframe: Muestra el objeto como un wireframe (malla de líneas).
  • map: Aplicar una textura al material.

3.3.2 MeshLambertMaterial

Un material que responde a la iluminación de manera difusa. Es ideal para superficies mate sin reflejos brillantes.

Sintaxis:

const material = new THREE.MeshLambertMaterial({ color: 0x00ff00 });

Propiedades comunes:

  • color: Color del material.
  • emissive: Color que el material emite (autoiluminación).
  • map: Aplicar una textura al material.

3.3.3 MeshPhongMaterial

Un material que responde a la iluminación con reflejos especulares, proporcionando un aspecto brillante y realista.

Sintaxis:

const material = new THREE.MeshPhongMaterial({ color: 0x0000ff, shininess: 100 });

Propiedades comunes:

  • color: Color del material.
  • shininess: Controla el brillo de los reflejos especulares.
  • specular: Color de los reflejos especulares.
  • map: Aplicar una textura al material.

3.3.4 MeshStandardMaterial

Un material físico estándar que utiliza el modelo PBR (Physically Based Rendering) para lograr resultados altamente realistas.

Sintaxis:

const material = new THREE.MeshStandardMaterial({ color: 0xffffff, metalness: 0.5, roughness: 0.5 });

Propiedades comunes:

  • color: Color del material.
  • metalness: Define si el material es metálico.
  • roughness: Define qué tan rugosa es la superficie.
  • map: Aplicar una textura al material.
  • normalMap: Aplicar un mapa de normales para simular detalles en la superficie.
  • envMap: Mapa de entorno para reflejos.

3.4 Aplicación de Colores y Texturas

3.4.1 Aplicar Color Sólido

Puedes definir un color sólido para un material utilizando la propiedad color.

Ejemplo:

const material = new THREE.MeshBasicMaterial({ color: 0xff5733 }); // Naranja rojizo

3.4.2 Aplicar Texturas

Las texturas son imágenes que se aplican a las superficies de los objetos para agregar detalles visuales. Three.js utiliza el cargador de texturas (THREE.TextureLoader) para cargar y aplicar texturas a los materiales.

Paso 1: Cargar la textura

const textureLoader = new THREE.TextureLoader();
const textura = textureLoader.load('ruta/a/tu/textura.jpg');

Paso 2: Aplicar la textura al material

const material = new THREE.MeshBasicMaterial({ map: textura });

Ejemplo Completo:

const textureLoader = new THREE.TextureLoader();
const textura = textureLoader.load('textures/brick_diffuse.jpg'); // Ruta a la textura

const material = new THREE.MeshBasicMaterial({ map: textura });

const geometry = new THREE.BoxGeometry(1, 1, 1);
const cubo = new THREE.Mesh(geometry, material);
scene.add(cubo);

3.4.3 Uso de Mapas de Materiales

Además del mapa de color (map), existen otros tipos de mapas que puedes utilizar para mejorar la apariencia de tus materiales:

  • Normal Map (normalMap): Simula detalles en la superficie sin aumentar el número de polígonos.

Ejemplo:

javascript const normalMap = textureLoader.load('textures/brick_normal.jpg'); const material = new THREE.MeshStandardMaterial({ map: textura, normalMap: normalMap });

  • Specular Map (specularMap): Define las áreas que reflejan la luz de manera especular.

  • Emissive Map (emissiveMap): Define las áreas que emiten luz.

  • Displacement Map (displacementMap): Desplaza los vértices de la geometría según la textura, creando relieves.

3.5 Ejemplo Completo: Cubo con Textura

A continuación, se presenta un ejemplo completo que crea un cubo y le aplica una textura de ladrillo, utilizando un material que responde a la iluminación para obtener un efecto más realista.

3.5.1 Estructura de Carpetas

mi-escena-textura-threejs/
├── index.html
├── js/
│   └── three.min.js
├── textures/
│   ├── brick_diffuse.jpg
│   └── brick_normal.jpg

3.5.2 Contenido de index.html

<!DOCTYPE html>
<html lang="es">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Cubo con Textura 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>
    <script>
        // 1. Crear la escena
        const scene = new THREE.Scene();

        // 2. Crear la cámara
        const camera = new THREE.PerspectiveCamera(
            75,
            window.innerWidth / window.innerHeight,
            0.1,
            1000
        );

        // 3. Crear el renderizador
        const renderer = new THREE.WebGLRenderer({ antialias: true });
        renderer.setSize(window.innerWidth, window.innerHeight);
        renderer.setPixelRatio(window.devicePixelRatio);
        renderer.shadowMap.enabled = true; // Habilitar sombras
        document.body.appendChild(renderer.domElement);

        // 4. Añadir una luz ambiental
        const ambientLight = new THREE.AmbientLight(0xffffff, 0.5); // Luz suave
        scene.add(ambientLight);

        // 5. Añadir una luz direccional
        const directionalLight = new THREE.DirectionalLight(0xffffff, 1);
        directionalLight.position.set(5, 10, 7.5);
        directionalLight.castShadow = true; // Habilitar sombras para esta luz
        scene.add(directionalLight);

        // 6. Cargar texturas
        const textureLoader = new THREE.TextureLoader();
        const texturaDifusa = textureLoader.load('textures/brick_diffuse.jpg'); // Textura difusa
        const texturaNormal = textureLoader.load('textures/brick_normal.jpg'); // Mapa de normales

        // 7. Crear el material
        const material = new THREE.MeshStandardMaterial({
            map: texturaDifusa,
            normalMap: texturaNormal
        });

        // 8. Crear la geometría y la malla
        const geometry = new THREE.BoxGeometry(2, 2, 2);
        const cubo = new THREE.Mesh(geometry, material);
        cubo.castShadow = true; // El cubo puede proyectar sombras
        cubo.receiveShadow = true; // El cubo puede recibir sombras
        scene.add(cubo);

        // 9. Crear el suelo
        const sueloGeometry = new THREE.PlaneGeometry(10, 10);
        const sueloMaterial = new THREE.MeshStandardMaterial({ color: 0x808080 });
        const suelo = new THREE.Mesh(sueloGeometry, sueloMaterial);
        suelo.rotation.x = -Math.PI / 2; // Rotar para que quede horizontal
        suelo.position.y = -2; // Posicionar debajo del cubo
        suelo.receiveShadow = true; // El suelo puede recibir sombras
        scene.add(suelo);

        // 10. Posicionar la cámara
        camera.position.set(5, 5, 5);
        camera.lookAt(cubo.position);

        // 11. Función de animación
        function animate() {
            requestAnimationFrame(animate);

            // Rotar el cubo
            cubo.rotation.x += 0.005;
            cubo.rotation.y += 0.005;

            // Renderizar la escena
            renderer.render(scene, camera);
        }

        animate();

        // 12. 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>

3.5.3 Explicación del Código

  1. Escena y Cámara:
  2. Se crea una escena y una cámara en perspectiva.
  3. La cámara está posicionada en (5, 5, 5) y mira hacia el cubo.

  4. Renderizador:

  5. Se configura el renderizador para ocupar toda la ventana y se habilitan las sombras.

  6. Iluminación:

  7. Luz Ambiental (AmbientLight): Proporciona una iluminación suave y uniforme en toda la escena.
  8. Luz Direccional (DirectionalLight): Simula una fuente de luz distante, como el sol, y está configurada para proyectar sombras.

  9. Texturas:

  10. Se cargan una textura difusa (brick_diffuse.jpg) y un mapa de normales (brick_normal.jpg) para darle detalles adicionales al material.

  11. Material y Malla:

  12. Se crea un MeshStandardMaterial que utiliza las texturas cargadas.
  13. Se aplica este material a una geometría de caja (BoxGeometry) para crear la malla del cubo.
  14. El cubo está configurado para proyectar y recibir sombras.

  15. Suelo:

  16. Se crea un plano que actúa como suelo, con un color gris.
  17. El suelo está rotado para quedar horizontal y posicionado debajo del cubo.
  18. Está configurado para recibir sombras.

  19. Animación:

  20. Se define una función animate que rota el cubo ligeramente en cada frame y renderiza la escena.

  21. Redimensionamiento:

  22. Se añade un evento que actualiza el tamaño del renderizador y la cámara cuando la ventana del navegador cambia de tamaño.

3.5.4 Recursos de Texturas

Para este ejemplo, necesitarás dos texturas:

  1. brick_diffuse.jpg: Una imagen que representa el color base de los ladrillos.
  2. brick_normal.jpg: Una imagen en escala de grises que representa las normales para simular detalles en la superficie.

Puedes encontrar texturas gratuitas en sitios como CC0 Textures o Textures.com.

3.6 Manipulación de Materiales

3.6.1 Cambiar Propiedades de Materiales en Tiempo de Ejecución

Puedes cambiar las propiedades de un material en tiempo real para crear efectos dinámicos.

Ejemplo: Cambiar el color de un material al hacer clic

cubo.material.color.set(0xff0000); // Cambia el color del cubo a rojo

3.6.2 Transparencia

Puedes hacer que un material sea transparente ajustando la propiedad transparent y opacity.

Ejemplo: Material Transparente

const materialTransparente = new THREE.MeshBasicMaterial({
    color: 0x00ff00,
    transparent: true,
    opacity: 0.5
});

3.6.3 Wireframe

Puedes visualizar la estructura de una geometría como un wireframe (malla de líneas) estableciendo la propiedad wireframe.

Ejemplo: Material Wireframe

const materialWireframe = new THREE.MeshBasicMaterial({
    color: 0xffffff,
    wireframe: true
});

3.7 Geometrías Compuestas y Agrupación de Objetos

Three.js permite crear geometrías más complejas combinando múltiples geometrías básicas o agrupando objetos.

3.7.1 Unión de Geometrías

Puedes combinar varias geometrías en una sola utilizando THREE.Group o THREE.Object3D.

Ejemplo: Crear una figura compuesta de dos cajas

const grupo = new THREE.Group();

const geometria1 = new THREE.BoxGeometry(1, 1, 1);
const material1 = new THREE.MeshBasicMaterial({ color: 0xff0000 });
const cubo1 = new THREE.Mesh(geometria1, material1);
cubo1.position.set(-1.5, 0, 0);

const geometria2 = new THREE.BoxGeometry(1, 1, 1);
const material2 = new THREE.MeshBasicMaterial({ color: 0x0000ff });
const cubo2 = new THREE.Mesh(geometria2, material2);
cubo2.position.set(1.5, 0, 0);

grupo.add(cubo1);
grupo.add(cubo2);

scene.add(grupo);

3.7.2 Uso de THREE.InstancedMesh

Para optimizar la renderización de múltiples instancias de la misma geometría y material, puedes usar THREE.InstancedMesh.

Ejemplo: Crear 100 esferas instanciadas

const cantidad = 100;
const geometria = new THREE.SphereGeometry(0.5, 16, 16);
const material = new THREE.MeshStandardMaterial({ color: 0x00ff00 });

const instancias = new THREE.InstancedMesh(geometria, material, cantidad);

for (let i = 0; i < cantidad; i++) {
    const matriz = new THREE.Matrix4();
    matriz.setPosition(
        Math.random() * 20 - 10,
        Math.random() * 20 - 10,
        Math.random() * 20 - 10
    );
    instancias.setMatrixAt(i, matriz);
}

scene.add(instancias);