6. Creación y Manipulación de Objetos 3D
6.1 Introducción a las Mallas (THREE.Mesh)
En Three.js, las mallas (Mesh) son los objetos básicos que representan formas 3D en una escena. Una malla combina una geometría (la forma) con un material (la apariencia visual), permitiendo así que los objetos tengan una forma definida y una apariencia determinada.
6.2 Creación de Mallas (THREE.Mesh)
Una malla en Three.js se crea combinando una geometría con un material. A continuación, se muestra cómo crear y añadir una malla simple a una escena.
6.2.1 Sintaxis Básica de THREE.Mesh
const malla = new THREE.Mesh(geometria, material);
scene.add(malla);
geometria: Define la forma del objeto (por ejemplo,BoxGeometry,SphereGeometry, etc.).material: Define la apariencia del objeto (por ejemplo,MeshBasicMaterial,MeshStandardMaterial, etc.).
6.2.2 Ejemplo: Crear un Cubo Verde
A continuación, se presenta un ejemplo completo que crea un cubo verde y lo añade a la escena.
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<title>Malla Básica en Three.js</title>
<style>
body { margin: 0; }
canvas { display: block; }
</style>
</head>
<body>
<!-- Enlace a Three.js -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r152/three.min.js"></script>
<script>
// 1. Crear la escena
const escena = new THREE.Scene();
// 2. Crear la cámara
const camara = new THREE.PerspectiveCamera(
75, // Campo de visión
window.innerWidth / window.innerHeight, // Relación de aspecto
0.1, // Plano cercano
1000 // Plano lejano
);
camara.position.z = 5; // Posicionar la cámara
// 3. Crear el renderizador
const renderizador = new THREE.WebGLRenderer({ antialias: true });
renderizador.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderizador.domElement);
// 4. Crear la geometría y el material
const geometriaCubo = new THREE.BoxGeometry(1, 1, 1); // Geometría de caja
const materialVerde = new THREE.MeshBasicMaterial({ color: 0x00ff00 }); // Material verde básico
// 5. Crear la malla y añadirla a la escena
const cubo = new THREE.Mesh(geometriaCubo, materialVerde);
escena.add(cubo);
// 6. Función de animación
function animar() {
requestAnimationFrame(animar);
// Rotar el cubo
cubo.rotation.x += 0.01;
cubo.rotation.y += 0.01;
// Renderizar la escena
renderizador.render(escena, camara);
}
animar(); // Iniciar la animación
// 7. Manejar el redimensionamiento de la ventana
window.addEventListener('resize', () => {
const ancho = window.innerWidth;
const alto = window.innerHeight;
renderizador.setSize(ancho, alto);
camara.aspect = ancho / alto;
camara.updateProjectionMatrix();
});
</script>
</body>
</html>
Resultado Esperado:
Al abrir el archivo HTML en tu navegador, deberías ver un cubo verde girando en el centro de la pantalla.
6.3 Transformaciones de Objetos: Traslación, Rotación y Escalado
Las transformaciones permiten modificar la posición, orientación y tamaño de los objetos en la escena. Three.js proporciona métodos y propiedades para aplicar estas transformaciones de manera sencilla.
6.3.1 Traslación (Movimiento)
Descripción:
La traslación mueve un objeto de un lugar a otro en el espacio 3D.
Propiedades Principales:
position.x: Movimiento a lo largo del eje X.position.y: Movimiento a lo largo del eje Y.position.z: Movimiento a lo largo del eje Z.
Ejemplo: Mover un Cubo hacia la Derecha y Arriba
cubo.position.x += 2; // Mover 2 unidades a la derecha
cubo.position.y += 1; // Mover 1 unidad hacia arriba
6.3.2 Rotación
Descripción:
La rotación gira un objeto alrededor de uno o más ejes.
Propiedades Principales:
rotation.x: Rotación alrededor del eje X (en radianes).rotation.y: Rotación alrededor del eje Y (en radianes).rotation.z: Rotación alrededor del eje Z (en radianes).
Ejemplo: Rotar un Cubo 45 Grados en el Eje Y
const grados = 45;
const radianes = grados * (Math.PI / 180);
cubo.rotation.y += radianes; // Rotar 45 grados en el eje Y
6.3.3 Escalado
Descripción:
El escalado cambia el tamaño de un objeto en una o más direcciones.
Propiedades Principales:
scale.x: Escalado a lo largo del eje X.scale.y: Escalado a lo largo del eje Y.scale.z: Escalado a lo largo del eje Z.
Ejemplo: Escalar un Cubo al Doble de su Tamaño Original
cubo.scale.set(2, 2, 2); // Escalar en X, Y y Z al doble
6.3.4 Métodos de Transformación
Además de las propiedades mencionadas, Three.js ofrece métodos para aplicar transformaciones de manera más controlada.
translateX(distance): Trasladar el objeto a lo largo del eje X.translateY(distance): Trasladar el objeto a lo largo del eje Y.translateZ(distance): Trasladar el objeto a lo largo del eje Z.rotateX(angle): Rotar el objeto alrededor del eje X.rotateY(angle): Rotar el objeto alrededor del eje Y.rotateZ(angle): Rotar el objeto alrededor del eje Z.scale.set(x, y, z): Establecer el escalado en los tres ejes.
Ejemplo: Aplicar Transformaciones Usando Métodos
// Trasladar
cubo.translateX(1); // Mover 1 unidad a la derecha
// Rotar
cubo.rotateY(Math.PI / 4); // Rotar 45 grados alrededor del eje Y
// Escalar
cubo.scale.set(1.5, 1.5, 1.5); // Escalar al 150% en todos los ejes
6.4 Agrupar Objetos (THREE.Group)
La agrupación de objetos permite manejar múltiples mallas como una sola entidad, facilitando su manipulación conjunta. Esto es útil para crear estructuras complejas o para aplicar transformaciones a múltiples objetos simultáneamente.
6.4.1 Crear y Utilizar un Grupo
Sintaxis Básica:
const grupo = new THREE.Group();
grupo.add(objeto1);
grupo.add(objeto2);
escena.add(grupo);
Ejemplo: Agrupar Dos Cubos de Colores Diferentes
// 1. Crear la geometría y materiales para dos cubos
const geometriaCubo = new THREE.BoxGeometry(1, 1, 1);
const materialRojo = new THREE.MeshBasicMaterial({ color: 0xff0000 }); // Rojo
const materialAzul = new THREE.MeshBasicMaterial({ color: 0x0000ff }); // Azul
// 2. Crear dos mallas
const cuboRojo = new THREE.Mesh(geometriaCubo, materialRojo);
const cuboAzul = new THREE.Mesh(geometriaCubo, materialAzul);
// 3. Posicionar los cubos
cuboRojo.position.x = -1.5; // A la izquierda
cuboAzul.position.x = 1.5; // A la derecha
// 4. Crear un grupo y añadir los cubos
const grupoCubos = new THREE.Group();
grupoCubos.add(cuboRojo);
grupoCubos.add(cuboAzul);
// 5. Añadir el grupo a la escena
escena.add(grupoCubos);
// 6. Aplicar una transformación al grupo (por ejemplo, rotación)
function animar() {
requestAnimationFrame(animar);
grupoCubos.rotation.y += 0.01; // Rotar el grupo alrededor del eje Y
renderizador.render(escena, camara);
}
animar();
Resultado Esperado:
Dos cubos (uno rojo y otro azul) agrupados y rotando juntos alrededor del eje Y.
6.4.3 Anidar Grupos
Los grupos pueden contener otros grupos, permitiendo una jerarquía de objetos. Esto es útil para estructuras complejas donde diferentes partes tienen transformaciones independientes.
Ejemplo: Grupo con Subgrupos
// Crear grupos principales
const grupoPrincipal = new THREE.Group();
const subgrupoIzquierdo = new THREE.Group();
const subgrupoDerecho = new THREE.Group();
// Crear mallas para cada subgrupo
const cuboVerde = new THREE.Mesh(new THREE.BoxGeometry(1, 1, 1), new THREE.MeshBasicMaterial({ color: 0x00ff00 }));
const cuboAmarillo = new THREE.Mesh(new THREE.BoxGeometry(1, 1, 1), new THREE.MeshBasicMaterial({ color: 0xffff00 }));
// Añadir mallas a los subgrupos
subgrupoIzquierdo.add(cuboVerde);
subgrupoDerecho.add(cuboAmarillo);
// Posicionar los subgrupos
subgrupoIzquierdo.position.x = -2;
subgrupoDerecho.position.x = 2;
// Añadir subgrupos al grupo principal
grupoPrincipal.add(subgrupoIzquierdo);
grupoPrincipal.add(subgrupoDerecho);
// Añadir el grupo principal a la escena
escena.add(grupoPrincipal);
// Aplicar transformaciones al grupo principal
function animar() {
requestAnimationFrame(animar);
grupoPrincipal.rotation.y += 0.005; // Rotar el grupo principal
subgrupoIzquierdo.rotation.x += 0.01; // Rotar subgrupo izquierdo
subgrupoDerecho.rotation.z += 0.01; // Rotar subgrupo derecho
renderizador.render(escena, camara);
}
animar();
Resultado Esperado:
Un grupo principal rotando lentamente alrededor del eje Y, mientras que cada subgrupo (cada uno con un cubo de color diferente) rota de manera independiente alrededor de sus propios ejes.
6.5 Manipulación Dinámica de Objetos
Además de las transformaciones estáticas, es posible manipular objetos de manera dinámica en respuesta a eventos del usuario o a otras condiciones en tiempo real.
6.5.1 Escuchar Eventos del Usuario
Puedes modificar las propiedades de los objetos en respuesta a eventos como clics, movimientos del ratón, o entradas del teclado.
Ejemplo: Cambiar el Color de un Cubo al Hacer Clic
// 1. Crear el cubo con MeshStandardMaterial para que pueda reaccionar a la luz
const materialCubo = new THREE.MeshStandardMaterial({ color: 0x00ff00 });
const cuboInteractivo = new THREE.Mesh(new THREE.BoxGeometry(1, 1, 1), materialCubo);
escena.add(cuboInteractivo);
// 2. Añadir una luz para que el material reaccione a la iluminación
const luzPuntual = new THREE.PointLight(0xffffff, 1);
luzPuntual.position.set(5, 5, 5);
escena.add(luzPuntual);
// 3. Detectar clics en el cubo
window.addEventListener('click', () => {
// Generar un color aleatorio
const colorAleatorio = Math.random() * 0xffffff;
cuboInteractivo.material.color.set(colorAleatorio);
});
Resultado Esperado:
Al hacer clic en cualquier parte de la ventana, el cubo cambiará a un color aleatorio.
6.5.2 Animaciones Basadas en Eventos
Puedes crear animaciones que respondan a eventos específicos, como mover un objeto cuando el usuario presiona una tecla.
Ejemplo: Mover un Cubo hacia Arriba al Presionar la Tecla "W"
// 1. Crear el cubo
const geometriaCubo = new THREE.BoxGeometry(1, 1, 1);
const materialCubo = new THREE.MeshStandardMaterial({ color: 0x0000ff });
const cuboMovil = new THREE.Mesh(geometriaCubo, materialCubo);
escena.add(cuboMovil);
// 2. Añadir una luz
const luzDireccional = new THREE.DirectionalLight(0xffffff, 1);
luzDireccional.position.set(5, 10, 7.5);
escena.add(luzDireccional);
// 3. Escuchar eventos de teclado
window.addEventListener('keydown', (evento) => {
const velocidad = 0.1;
switch(evento.key.toLowerCase()) {
case 'w':
cuboMovil.position.y += velocidad; // Mover hacia arriba
break;
case 's':
cuboMovil.position.y -= velocidad; // Mover hacia abajo
break;
case 'a':
cuboMovil.position.x -= velocidad; // Mover hacia la izquierda
break;
case 'd':
cuboMovil.position.x += velocidad; // Mover hacia la derecha
break;
default:
break;
}
});
Resultado Esperado:
Al presionar las teclas "W", "A", "S" o "D", el cubo se moverá hacia arriba, izquierda, abajo o derecha, respectivamente.
6.6 Creación de Objetos Compuestos
A menudo, necesitarás crear objetos más complejos combinando múltiples mallas y formas. Esto se logra utilizando grupos y jerarquías de objetos.
6.6.1 Ejemplo: Crear un Robot Básico
A continuación, se muestra cómo crear una estructura simple que representa un robot utilizando diferentes mallas agrupadas.
// 1. Crear el grupo principal del robot
const robot = new THREE.Group();
// 2. Crear la cabeza
const geometriaCabeza = new THREE.BoxGeometry(1, 1, 1);
const materialCabeza = new THREE.MeshStandardMaterial({ color: 0xffd700 }); // Dorado
const cabeza = new THREE.Mesh(geometriaCabeza, materialCabeza);
cabeza.position.y = 2; // Posicionar la cabeza arriba del cuerpo
// 3. Crear el cuerpo
const geometriaCuerpo = new THREE.BoxGeometry(2, 2, 1);
const materialCuerpo = new THREE.MeshStandardMaterial({ color: 0x00bfff }); // Azul
const cuerpo = new THREE.Mesh(geometriaCuerpo, materialCuerpo);
cuerpo.position.y = 0;
// 4. Crear los brazos
const geometriaBrazo = new THREE.BoxGeometry(0.5, 2, 0.5);
const materialBrazo = new THREE.MeshStandardMaterial({ color: 0x8b4513 }); // Marrón
const brazoIzquierdo = new THREE.Mesh(geometriaBrazo, materialBrazo);
const brazoDerecho = new THREE.Mesh(geometriaBrazo, materialBrazo);
brazoIzquierdo.position.set(-1.5, 1, 0); // Posicionar a la izquierda
brazoDerecho.position.set(1.5, 1, 0); // Posicionar a la derecha
// 5. Crear las piernas
const geometriaPierna = new THREE.BoxGeometry(0.5, 2, 0.5);
const materialPierna = new THREE.MeshStandardMaterial({ color: 0x2e8b57 }); // Verde
const piernaIzquierda = new THREE.Mesh(geometriaPierna, materialPierna);
const piernaDerecha = new THREE.Mesh(geometriaPierna, materialPierna);
piernaIzquierda.position.set(-0.5, -2, 0); // Posicionar a la izquierda
piernaDerecha.position.set(0.5, -2, 0); // Posicionar a la derecha
// 6. Añadir todas las partes al grupo del robot
robot.add(cabeza);
robot.add(cuerpo);
robot.add(brazoIzquierdo);
robot.add(brazoDerecho);
robot.add(piernaIzquierda);
robot.add(piernaDerecha);
// 7. Añadir el robot a la escena
escena.add(robot);
// 8. Función de animación para rotar el robot
function animar() {
requestAnimationFrame(animar);
// Rotar todo el robot
robot.rotation.y += 0.01;
renderizador.render(escena, camara);
}
animar();
Resultado Esperado:
Un robot básico compuesto por una cabeza, cuerpo, brazos y piernas, rotando lentamente alrededor del eje Y.
6.7 Manipulación de Mallas Individuales Dentro de un Grupo
Cuando trabajas con grupos, puedes manipular individualmente las mallas dentro del grupo sin afectar al resto de los objetos.
Ejemplo: Hacer que los Brazos del Robot Se Muevan Independientemente
// Suponiendo que ya tienes el grupo 'robot' con 'brazoIzquierdo' y 'brazoDerecho'
// 1. Crear una función para mover los brazos
function moverBrazos() {
brazoIzquierdo.rotation.z = Math.sin(Date.now() * 0.005) * 0.5;
brazoDerecho.rotation.z = Math.cos(Date.now() * 0.005) * 0.5;
}
// 2. Actualizar la función de animación
function animar() {
requestAnimationFrame(animar);
// Rotar todo el robot
robot.rotation.y += 0.01;
// Mover los brazos
moverBrazos();
renderizador.render(escena, camara);
}
animar();
Resultado Esperado:
Mientras el robot rota, los brazos se mueven de manera independiente, simulando un movimiento de balanceo.
6.8 Mejores Prácticas
Uso de Helpers para Depuración:
Utiliza helpers como THREE.AxesHelper, THREE.GridHelper o THREE.CameraHelper para visualizar e inspeccionar la orientación y posición de objetos y cámaras durante el desarrollo.
Ejemplo: Añadir un AxesHelper y un GridHelper
const axesHelper = new THREE.AxesHelper(5);
escena.add(axesHelper);
// Añadir un GridHelper para visualizar una cuadrícula en el suelo
const gridHelper = new THREE.GridHelper(20, 20);
gridHelper.position.y = -1; // Posicionar al nivel del suelo
escena.add(gridHelper);
6.9 Ejemplo Completo: Escena con Mallas, Transformaciones y Agrupación
A continuación, se presenta un ejemplo que integra todos los conceptos abordados en este tema, creando una escena con múltiples mallas, aplicando transformaciones y organizándolas en grupos.
6.9.1 Estructura de Carpetas
mi-escena-objetos-threejs/
├── index.html
└── js/
└── three.min.js
6.9.2 Contenido de index.html
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<title>Creación y Manipulación de Objetos 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 escena = new THREE.Scene();
escena.background = new THREE.Color(0xeeeeee);
// 2. Crear la cámara
const camara = new THREE.PerspectiveCamera(
75,
window.innerWidth / window.innerHeight,
0.1,
1000
);
camara.position.set(0, 5, 10);
camara.lookAt(0, 0, 0);
// 3. Crear el renderizador
const renderizador = new THREE.WebGLRenderer({ antialias: true });
renderizador.setSize(window.innerWidth, window.innerHeight);
renderizador.shadowMap.enabled = true; // Habilitar sombras
renderizador.shadowMap.type = THREE.PCFSoftShadowMap;
document.body.appendChild(renderizador.domElement);
// 4. Añadir una luz ambiental
const ambientLight = new THREE.AmbientLight(0x404040, 1); // Luz ambiental suave
escena.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; // 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;
escena.add(directionalLight);
// 6. Crear un grupo de cubos rotatorios
const grupoCubos = new THREE.Group();
// Crear varias mallas de cubos con diferentes colores
const colores = [0xff0000, 0x00ff00, 0x0000ff, 0xffff00, 0xff00ff];
colores.forEach((color, index) => {
const geometria = new THREE.BoxGeometry(1, 1, 1);
const material = new THREE.MeshStandardMaterial({ color: color });
const cubo = new THREE.Mesh(geometria, material);
cubo.position.x = (index - 2) * 2; // Espaciarlos a lo largo del eje X
cubo.castShadow = true;
cubo.receiveShadow = true;
grupoCubos.add(cubo);
});
escena.add(grupoCubos);
// 7. Crear el suelo que recibe sombras
const sueloGeometria = new THREE.PlaneGeometry(20, 20);
const sueloMaterial = new THREE.MeshStandardMaterial({ color: 0x808080 });
const suelo = new THREE.Mesh(sueloGeometria, sueloMaterial);
suelo.rotation.x = -Math.PI / 2; // Rotar para que quede horizontal
suelo.position.y = -1;
suelo.receiveShadow = true;
escena.add(suelo);
// 8. Crear helpers para depuración
const axesHelper = new THREE.AxesHelper(5);
escena.add(axesHelper);
const gridHelper = new THREE.GridHelper(20, 20);
gridHelper.position.y = -1;
escena.add(gridHelper);
// 9. Función de animación
function animar() {
requestAnimationFrame(animar);
// Rotar el grupo de cubos
grupoCubos.rotation.y += 0.01;
// Rotar cada cubo individualmente
grupoCubos.children.forEach((cubo, index) => {
cubo.rotation.x += 0.02;
cubo.rotation.y += 0.02;
});
renderizador.render(escena, camara);
}
animar();
// 10. Manejar el redimensionamiento de la ventana
window.addEventListener('resize', () => {
const ancho = window.innerWidth;
const alto = window.innerHeight;
renderizador.setSize(ancho, alto);
camara.aspect = ancho / alto;
camara.updateProjectionMatrix();
});
</script>
</body>
</html>
Explicación del Código:
- Escena y Cámara:
- Se crea una escena con un fondo 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. - Se habilitan las sombras y se configura el tipo de sombra (
PCFSoftShadowMap) para obtener sombras más suaves. -
El renderizador se ajusta al tamaño de la ventana y se añade al documento HTML.
-
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. -
Grupo de Cubos:
- Se crea un grupo (
THREE.Group) que contiene varios cubos de diferentes colores. - Cada cubo se posiciona a lo largo del eje X para espaciarlo.
- Los cubos están configurados para proyectar y recibir sombras.
-
El grupo completo se añade a la escena.
-
Suelo:
- Se crea un plano grande que actúa como suelo, rotado para quedar horizontal y posicionado debajo de los cubos.
-
El suelo está configurado para recibir sombras.
-
Helpers para Depuración:
- AxesHelper: Visualiza los ejes X, Y y Z en la escena.
-
GridHelper: Muestra una cuadrícula en el suelo para facilitar la percepción de la escala y la posición de los objetos.
-
Animación:
- Se define una función
animarque rota el grupo de cubos alrededor del eje Y y, además, rota cada cubo individualmente alrededor de sus ejes X e Y. -
La función se llama en cada frame utilizando
requestAnimationFramepara crear una animación fluida. -
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.
Resultado Esperado:
Una escena con varios cubos de colores diferentes agrupados y rotando juntos alrededor del eje Y, mientras que cada cubo también rota individualmente. El suelo y los helpers proporcionan una referencia visual clara de la orientación y escala de los objetos.