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
- Escena y Cámara:
- Se crea una escena y una cámara en perspectiva.
-
La cámara está posicionada en (5, 5, 5) y mira hacia el cubo.
-
Renderizador:
-
Se configura el renderizador para ocupar toda la ventana y se habilitan las sombras.
-
Iluminación:
- Luz Ambiental (
AmbientLight): Proporciona una iluminación suave y uniforme en toda la escena. -
Luz Direccional (
DirectionalLight): Simula una fuente de luz distante, como el sol, y está configurada para proyectar sombras. -
Texturas:
-
Se cargan una textura difusa (
brick_diffuse.jpg) y un mapa de normales (brick_normal.jpg) para darle detalles adicionales al material. -
Material y Malla:
- Se crea un
MeshStandardMaterialque utiliza las texturas cargadas. - Se aplica este material a una geometría de caja (
BoxGeometry) para crear la malla del cubo. -
El cubo está configurado para proyectar y recibir sombras.
-
Suelo:
- Se crea un plano que actúa como suelo, con un color gris.
- El suelo está rotado para quedar horizontal y posicionado debajo del cubo.
-
Está configurado para recibir sombras.
-
Animación:
-
Se define una función
animateque rota el cubo ligeramente en cada frame y renderiza la escena. -
Redimensionamiento:
- 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:
- brick_diffuse.jpg: Una imagen que representa el color base de los ladrillos.
- 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);