introducción

1. ¿Qué es Three.js?

Three.js es una librería JavaScript de código abierto que permite crear y mostrar gráficos 3D en el navegador. Internamente usa WebGL, la API del navegador para gráficos de alto rendimiento por GPU, pero nos oculta toda su complejidad.

WebGL puro requiere escribir shaders en GLSL, gestionar buffers de vértices, matrices de transformación y mucho más. Three.js abstrae todo eso: creas objetos con código JavaScript legible y la librería traduce todo a WebGL por ti.

Jerarquía de una escena Three.js

Todos los elementos de Three.js se organizan en una estructura de árbol:

🌐 THREE.Scene El contenedor principal. Todo lo visible vive aquí.
├── añadir con scene.add(objeto)
📷 PerspectiveCamera Define el punto de vista. No se añade a la escena.
├──
💡 AmbientLight / DirectionalLight Fuentes de luz. Se añaden a la escena.
├──
🧊 THREE.Mesh Objeto 3D visible = Geometría + Material.
├──
📐 BoxGeometry La forma del objeto (vértices, caras).
└──
🎨 MeshStandardMaterial El aspecto del objeto (color, textura, brillo).
└──
🖥 WebGLRenderer El "pintor". Dibuja la escena en el <canvas>.
setup

2. Configuración inicial

Three.js se puede incluir de dos formas: via CDN con un <script> clásico, o usando ES Modules con import. En proyectos modernos se prefieren los módulos.

Opción A — Import Map (recomendado para este curso)

<!-- 1. Declara el mapa de importaciones en el <head> -->
<script type="importmap">
  {
    "imports": {
      "three": "https://cdn.jsdelivr.net/npm/three@0.160.0/build/three.module.js",
      "three/addons/": "https://cdn.jsdelivr.net/npm/three@0.160.0/examples/jsm/"
    }
  }
</script>

<!-- 2. En el script, importa lo que necesites -->
<script type="module">
  import * as THREE from 'three';
  import { OrbitControls } from 'three/addons/controls/OrbitControls.js';

  // Tu código Three.js va aquí...
</script>

type="module" es obligatorio. Los scripts de tipo módulo tienen su propio scope, se ejecutan diferido y permiten usar import. Sin él, import causará un error de sintaxis.

Estructura HTML base

<!-- El canvas es donde Three.js dibuja. Puede ir en cualquier parte del body. -->
<canvas id="mi-canvas"></canvas>

<!-- Estilos para que el canvas ocupe toda la ventana -->
<style>
  * { margin: 0; padding: 0; box-sizing: border-box; }
  body { overflow: hidden; background: #000; }
  canvas { display: block; }
</style>
núcleo

3. Escena, Cámara y Renderer

Estos tres elementos son el mínimo imprescindible de cualquier proyecto Three.js. Sin los tres no hay nada que mostrar.

import * as THREE from 'three';

// ── 1. ESCENA ─────────────────────────────────────────────────
// La escena es el "mundo" donde colocamos todos los objetos.
const scene = new THREE.Scene();
scene.background = new THREE.Color('#0a0a12'); // color de fondo

// ── 2. CÁMARA ─────────────────────────────────────────────────
// PerspectiveCamera simula perspectiva humana (objetos lejanos son más pequeños).
// Parámetros: fov (campo visual°), aspect (ancho/alto), near, far
const camera = new THREE.PerspectiveCamera(
  75,                               // FOV: 75° es un valor natural
  window.innerWidth / window.innerHeight, // relación de aspecto
  0.1,                              // plano cercano (near)
  1000                              // plano lejano (far)
);
camera.position.set(0, 2, 5); // posición X, Y, Z en el espacio 3D
camera.lookAt(0, 0, 0);        // apunta al origen

// ── 3. RENDERER ───────────────────────────────────────────────
// WebGLRenderer dibuja la escena en un elemento .
const renderer = new THREE.WebGLRenderer({
  canvas: document.getElementById('mi-canvas'),
  antialias: true   // suaviza los bordes (antialiasing)
});
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setPixelRatio(window.devicePixelRatio); // resolución en pantallas Retina

// ── 4. PRIMER RENDER ──────────────────────────────────────────
renderer.render(scene, camera); // dibuja UNA vez

Demo en vivo — escena mínima con un cubo

Scene + Camera + Renderer + Mesh
🖱 Arrastra para orbitar · Scroll para zoom
geometrías

4. Geometrías primitivas

Three.js incluye muchas geometrías listas para usar. Todas heredan de BufferGeometry y definen la forma de un objeto mediante vértices, aristas y caras.

BoxGeometrynew THREE.BoxGeometry(w, h, d)
SphereGeometrynew THREE.SphereGeometry(r, seg, seg)
CylinderGeometrynew THREE.CylinderGeometry(r, r, h, seg)
TorusGeometrynew THREE.TorusGeometry(r, tube, seg, seg)
PlaneGeometrynew THREE.PlaneGeometry(w, h)
ConeGeometrynew THREE.ConeGeometry(r, h, seg)
// ── GEOMETRÍAS PRIMITIVAS ─────────────────────────────────────
// Cada geometría define la forma matemática del objeto.

const cubo      = new THREE.BoxGeometry(1, 1, 1);        // ancho, alto, profundidad
const esfera    = new THREE.SphereGeometry(0.5, 32, 32);  // radio, seg. horizontales, verticales
const plano     = new THREE.PlaneGeometry(10, 10);       // ancho, alto
const cilindro  = new THREE.CylinderGeometry(0.5, 0.5, 2, 32); // radioTop, radioBot, h, seg
const toro      = new THREE.TorusGeometry(0.5, 0.2, 16, 100); // radio, tubo, seg, seg
const cono      = new THREE.ConeGeometry(0.5, 1, 32);     // radio, altura, segmentos

// ¡Más segmentos = más suave, pero más costoso computacionalmente!
// SphereGeometry(0.5, 8, 8) → rugosa. SphereGeometry(0.5, 64, 64) → perfecta.
materiales

5. Materiales y Luces

El material define el aspecto visual del objeto. Algunos materiales reaccionan a la luz, otros no. Si usas MeshStandardMaterial y no hay luces en la escena, el objeto se verá completamente negro.

Material Necesita luz Uso típico
MeshBasicMaterial No Pruebas, wireframes, iconos. Siempre visible.
MeshNormalMaterial No Depuración. Colorea las normales de la geometría.
MeshLambertMaterial Superficies mate sin reflejos especulares. Rápido.
MeshPhongMaterial Superficies con brillo especular. Plástico, metal básico.
MeshStandardMaterial Recomendado. PBR: roughness + metalness. Más realista.
MeshPhysicalMaterial PBR avanzado: vidrio, refracción, clearcoat.

Demo en vivo — comparativa de materiales

MeshBasicMaterial · MeshNormalMaterial · MeshStandardMaterial
🖱 Arrastra para orbitar
// ── MATERIALES ────────────────────────────────────────────────

// MeshBasicMaterial: no reacciona a la luz, color plano
const matBasico = new THREE.MeshBasicMaterial({
  color: '#ff6b35',
  wireframe: false   // true → muestra solo las aristas
});

// MeshStandardMaterial: PBR (Physically Based Rendering)
// Necesita al menos una luz para verse correctamente.
const matStandard = new THREE.MeshStandardMaterial({
  color:     '#4aa8ff', // color base
  metalness: 0.3,       // 0 = plástico, 1 = metal puro
  roughness: 0.4        // 0 = espejo, 1 = completamente rugoso
});

// ── LUCES ─────────────────────────────────────────────────────

// AmbientLight: ilumina uniformemente todo, sin dirección ni sombras
const luzAmbiente = new THREE.AmbientLight('#ffffff', 0.5); // color, intensidad
scene.add(luzAmbiente);

// DirectionalLight: como el sol, rayos paralelos, produce sombras
const luzDireccional = new THREE.DirectionalLight('#ffffff', 1.5);
luzDireccional.position.set(5, 10, 7); // posición de la fuente de luz
scene.add(luzDireccional);

// PointLight: como una bombilla, emite en todas las direcciones
const luzPunto = new THREE.PointLight('#ff6b35', 2, 10); // color, intensidad, distancia
luzPunto.position.set(-3, 2, 2);
scene.add(luzPunto);
transformaciones

6. Posición, Rotación y Escala

Todo objeto 3D en Three.js (Mesh, Camera, Light…) tiene tres propiedades de transformación: position, rotation y scale. Las tres son objetos THREE.Vector3 con ejes X, Y y Z.

// ── TRANSFORMACIONES DE UN MESH ───────────────────────────────
const mesh = new THREE.Mesh(geometria, material);

// POSICIÓN — en unidades de la escena (1 unit ≈ 1 metro)
mesh.position.set(2, 0, -1); // x, y, z
mesh.position.x = 2;          // también se puede eje a eje

// ROTACIÓN — en radianes (Math.PI = 180°)
mesh.rotation.set(0, Math.PI / 4, 0); // 45° sobre el eje Y
mesh.rotation.y += 0.01;              // acumular en cada frame → gira

// ESCALA — 1 = tamaño original
mesh.scale.set(2, 1, 0.5); // doble en X, mitad en Z
mesh.scale.setScalar(1.5); // escala uniforme

// JERARQUÍAS — agrupar objetos para transformarlos juntos
const grupo = new THREE.Group();
grupo.add(mesh1);
grupo.add(mesh2);
grupo.position.y = 2; // mueve ambos meshes juntos
scene.add(grupo);

// CONVERSIÓN: grados → radianes
// const rad = THREE.MathUtils.degToRad(45); // → 0.785...

Demo en vivo — controles de transformación

position · rotation · scale
controles

7. OrbitControls

OrbitControls es un add-on de Three.js (no está en el bundle principal) que agrega navegación 3D interactiva: orbitar con el mouse, hacer zoom con la rueda y hacer panning con el botón derecho.

// ── IMPORTAR ORBITCONTROLS (es un add-on, no parte del core) ──
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';

// ── CREAR E INICIALIZAR ────────────────────────────────────────
const controls = new OrbitControls(camera, renderer.domElement);
// domElement es el  del renderer — los eventos de mouse van ahí

// ── OPCIONES COMUNES ──────────────────────────────────────────
controls.enableDamping  = true;  // inercia suave al soltar el mouse
controls.dampingFactor  = 0.05; // qué tan rápido frena (0.05 es suave)
controls.enableZoom     = true;  // zoom con rueda del mouse
controls.enablePan      = true;  // panning con botón derecho
controls.autoRotate     = false; // rotación automática continua
controls.autoRotateSpeed= 2.0;  // velocidad de autoRotate
controls.minDistance   = 2;     // distancia mínima de zoom
controls.maxDistance   = 20;    // distancia máxima de zoom
controls.maxPolarAngle  = Math.PI / 2; // ángulo vertical máximo (no gira debajo)

// ── MUY IMPORTANTE: actualizar en el loop ─────────────────────
// Si enableDamping = true, DEBES llamar controls.update() en cada frame
function animate() {
  requestAnimationFrame(animate);
  controls.update(); // ← obligatorio con damping
  renderer.render(scene, camera);
}
animate();

Controles del mouse con OrbitControls:
Botón izquierdo + arrastrar → orbitar (rotar la cámara alrededor)
Rueda del mouse → zoom in/out
Botón derecho + arrastrar → panning (mover la cámara lateralmente)
Dos dedos en touch → zoom. Un dedo → orbitar

animación

8. Loop de animación

requestAnimationFrame es la API del navegador para crear loops de animación sincronizados con la pantalla (60fps o más). Three.js lo usa internamente para mantener la escena en movimiento.

// ── EL LOOP DE ANIMACIÓN ──────────────────────────────────────
// requestAnimationFrame llama a la función antes de cada repintado
// de la pantalla (60fps en la mayoría de monitores).
// La función se llama a sí misma para crear el loop infinito.

const clock = new THREE.Clock(); // para medir el tiempo transcurrido

function animate() {
  requestAnimationFrame(animate); // ← pide el próximo frame

  const elapsed = clock.getElapsedTime(); // segundos desde que empezó
  const delta   = clock.getDelta();       // tiempo entre el frame anterior y este

  // ── Rotar el cubo en cada frame ──
  cubo.rotation.x += 0.01;           // velocidad en radianes/frame
  cubo.rotation.y += 0.01;

  // ── Animación con seno (oscilación suave) ──
  esfera.position.y = Math.sin(elapsed * 2) * 0.5; // sube y baja

  // ── Actualizar controles (si tienen damping) ──
  controls.update();

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

animate(); // ← arrancar el loop

// ── RESPONSIVE: adaptar al cambio de tamaño de ventana ────────
window.addEventListener('resize', () => {
  camera.aspect = window.innerWidth / window.innerHeight;
  camera.updateProjectionMatrix(); // obligatorio tras cambiar aspect
  renderer.setSize(window.innerWidth, window.innerHeight);
});

Demo en vivo — escena completa animada

Scene · Lights · Meshes · OrbitControls · requestAnimationFrame
🖱 Arrastra · Scroll · Click derecho
entregable

9. Proyecto Aplicado

🧊 Escena 3D interactiva desde cero

Construye una escena 3D completa usando todos los conceptos de la semana: múltiples objetos, materiales con luz, navegación con OrbitControls y al menos un objeto animado en el loop.

Requisitos mínimos

  • Configuración correcta: Scene, PerspectiveCamera, WebGLRenderer con antialias.
  • Al menos 3 geometrías primitivas distintas con materiales diferentes.
  • Iluminación con AmbientLight y al menos una DirectionalLight o PointLight.
  • OrbitControls con enableDamping: true para navegación suave.
  • Loop de animación con al menos un objeto que rote, oscile o se mueva.
  • Escena responsive: resize listener que actualiza cámara y renderer.
  • Un plano como "suelo" para dar contexto espacial a la escena.

Criterios de evaluación

🌐 Escena base 25 pts

  • Scene + Camera + Renderer correctos
  • Antialias activo
  • Canvas ocupa el viewport
  • Resize listener implementado

🧊 Objetos 3D 25 pts

  • ≥ 3 geometrías distintas
  • MeshStandardMaterial con color
  • Posiciones distintas en la escena
  • Al menos un Group con jerarquía

💡 Iluminación 25 pts

  • AmbientLight para iluminación base
  • DirectionalLight o PointLight
  • Los objetos reaccionan a la luz
  • roughness / metalness configurados

🎮 Interacción 25 pts

  • OrbitControls con damping
  • Loop de animación con rAF
  • Al menos un objeto animado
  • Código limpio y comentado

💡 Recursos clave: Documentación oficial de Three.js · Ejemplos oficiales · Discover Three.js (libro gratuito). El inspector de escena Three.js Developer Tools para Chrome es muy útil para depurar.