← Volver al sistema solar

Detrás del portafolio

Cómo hice este portafolio

Un sistema solar en 3D donde cada planeta es una sección. Aquí explico las decisiones técnicas: qué hace cada pieza y por qué está hecha así.

Three.js r160WebGL
GLSLshaders propios
Un solo HTMLsin build
Web Workertexturas procedurales

01Un solo archivo, sin herramientas de build

Todo el portafolio es un index.html con su CSS y su JavaScript dentro, más las imágenes. Three.js se carga como módulo ES desde un CDN con un import map, así que no hay npm, bundler ni paso de compilación: lo que se edita es lo que se publica.

02Texturas: primero pintadas, luego reales

Cada planeta arranca con una textura generada por código: ruido fractal (fBm) evaluado sobre la esfera y pasado por una rampa de colores propia de cada planeta. Se pinta en un Web Worker, así la página nunca se congela mientras se generan.

En paralelo se descargan los mapas reales de Solar System Scope (CC BY 4.0) en WebP: a 2048 px en escritorio y a 1024 px en móvil, unos 1,5 MB y 0,4 MB en total. Cuando cada uno llega, sustituye al pintado sin recompilar el shader, porque todos los materiales empiezan con un mapa blanco de 1×1 que solo se intercambia.

03Los planetas están donde están hoy

La posición de cada planeta sale de los elementos orbitales que publica la JPL de la NASA (Approximate Positions of the Planets). Para una fecha se calcula la anomalía media, se resuelve la ecuación de Kepler y se obtiene la longitud heliocéntrica real:

function helioLon([e0, e1, L0, L1, w0, w1], days) {
  const T = days / 36525;                    // siglos desde J2000
  const e = e0 + e1 * T, w = (w0 + w1 * T) * d2r;
  const M = (L0 + L1 * T) * d2r - w;         // anomalía media
  let E = M;
  for (let k = 0; k < 8; k++) E = M + e * Math.sin(E);   // Kepler
  return 2 * Math.atan2(Math.sqrt(1 + e) * Math.sin(E / 2),
                        Math.sqrt(1 - e) * Math.cos(E / 2)) + w;
}

La dirección es real; las distancias no, porque a escala real los planetas serían puntos invisibles. La barra de tiempo mueve ese reloj hacia delante o hacia atrás, hasta un año por segundo. La Luna también empieza en su posición real, y las lunas de los demás planetas giran con sus periodos reales, limitados en velocidad para que Fobos no parpadee cuando el reloj va rápido.

04El Sol

El Sol no tiene textura: es un shader que mezcla varias capas de ruido animado para la granulación y oscurece el borde del disco (limb darkening), como se ve en las fotos reales. Encima hay una corona animada en un plano que siempre mira a la cámara. En escritorio se añade un bloom (UnrealBloomPass) con un umbral alto, para que solo brille lo que de verdad es muy luminoso. Se carga después de la escena; si falla, el sistema se dibuja igual sin él.

05Un material para todos los planetas

Los planetas usan el MeshStandardMaterial de Three.js, extendido con onBeforeCompile para inyectar GLSL propio sin perder la iluminación física:

Los anillos hacen lo contrario: un rayo desde cada punto del anillo hacia el Sol que choca con la esfera del planeta dibuja su sombra, y la cara que no mira al Sol se oscurece porque solo le llega luz a través.

06Aterrizar en cada planeta

Al abrir una sección la cámara vuela hasta el planeta, gira hacia su lado iluminado y aterriza mirando al horizonte. Alrededor del punto de aterrizaje se construye el relieve de ese cuerpo (montañas, rocas, hielo, hierba en la Tierra) y se activa una luz direccional con sombras. El mapa de sombras solo se recalcula mientras estás en tierra.

07Rendimiento

08Lo que no es 3D

El texto está en español e inglés con un diccionario que traduce los nodos de texto de la página. El formulario de propuestas envía los mensajes a un canal de Discord, y la tarjeta de estado de Discord se actualiza en directo por WebSocket con Lanyard.