# Isla Robinson Crusoe en 3D: la isla real, en tiempo real, en el navegador

> Un render fotorrealista y geográficamente exacto del archipiélago Juan Fernández que corre en el navegador: datos satelitales reales, un océano simulado con olas que rompen en la costa real y nubes volumétricas.

- **Autor:** Hans Vergara (https://h4nsd3v.web.app/)
- **Publicado:** 8 de octubre de 2026
- **Etiquetas:** three.js, WebGL2, Python, GIS, Simulación
- **URL:** https://h4nsd3v.web.app/blog/isla-robinson-crusoe-3d/
- **Traducción:** https://h4nsd3v.web.app/en/blog/isla-robinson-crusoe-3d/index.md

## El desafío

Isla Robinson Crusoe está a unos 670 km del continente chileno, en el archipiélago Juan Fernández: acantilados de basalto que caen al Pacífico, quebradas cubiertas de bosque nativo y El Yunque, de 915 m, sobre Bahía Cumberland. Me propuse traerla al navegador **tal como es**: fotorrealista, con el norte y la escala correctos, recorrible desde una vista de todo el archipiélago hasta la orilla de una playa, y a 60 cuadros por segundo en un MacBook Air.

La regla que guió todo el proyecto: **nada de geografía inventada**. El relieve, la costa, la batimetría y la posición de cada lugar salen de datos reales. Lo procedural solo se permite como detalle bajo la resolución de los datos, como la forma de una ola, la textura de una roca o la malla de un árbol.

[Image: Bahía Cumberland y San Juan Bautista bajo El Yunque, renderizados en tiempo real]

*Bahía Cumberland y el pueblo de San Juan Bautista bajo El Yunque. La cámara reproduce la posición exacta de una foto de dron de referencia.*

## De los satélites a la escena

La mitad del trabajo ocurre antes de dibujar el primer píxel, en un pipeline de datos en Python. Un contrato compartido (`SPEC.md`) fija el sistema de coordenadas, una proyección Web Mercator local escalada con error menor al 0,1 %, y los formatos que el pipeline y el renderer deben respetar al pie de la letra.

- **Relieve:** el modelo de elevación Copernicus GLO-30 (30 m) se lleva a una grilla maestra de unos 4 m con una aproximación B-spline que nunca sobrepasa en los acantilados. La batimetría de GMRT y GEBCO se une a la costa con un ajuste de Poisson.
- **Imágenes:** un compuesto de Sentinel-2 sin nubes define el color real de la superficie (albedo). Sobre él se fusionan las mejores capturas de Esri, incluidas las históricas, elegidas por tener menos nubes, sombras y bruma, igualadas en color y con las uniones difuminadas.
- **Costa:** un clasificador sobre las imágenes refina la línea de costa a unos 2 m y produce la distancia a la costa, las playas, el bosque, la exposición al oleaje del suroeste y al viento del sureste, y el tipo de orilla: arena, bolones o acantilado.
- **Relieve fino:** donde hay capturas de mañana y de tarde, la *shape-from-shading* recupera las quebradas que el modelo de 30 m no ve, y la altura de los islotes se mide por la longitud de sus sombras.
- **El fondo marino:** la plataforma insular está calibrada con un dato curioso, el naufragio del crucero alemán **SMS Dresden**, hundido en 1915 a unos 70 m de profundidad frente a Cumberland.

Todo termina en una pirámide de teselas, de la vista general (z11) al detalle de 4 m (z17–z18). Las alturas se guardan en teselas de 65×65 con bordes idénticos bit a bit, para que no aparezcan grietas entre niveles. Las imágenes se comprimen en ASTC, lo que divide por cuatro la memoria de GPU.

## El render en el navegador

El renderer está escrito en TypeScript sobre three.js y WebGL2, organizado como *features* independientes: cielo, terreno, océano, nubes y vegetación.

- **Terreno:** un *quadtree* de niveles de detalle con *geomorphing*, sin saltos al acercarse. Se carga por prioridad de error en pantalla, con más detalle justo en la línea de costa, que es donde un error se nota más.
- **Cielo:** atmósfera física (Hillaire 2020) y sol calculado para la fecha y hora reales en la isla.
- **Océano:** el mar abierto es una FFT en GPU (Tessendorf) con cuatro cascadas y un espectro JONSWAP: el oleaje del suroeste de 12 s más el mar de viento del sureste. Cerca de la costa, las olas se **refractan sobre la batimetría real**, resolviendo la ecuación eikonal con *fast sweeping*: giran hacia la orilla, se acortan, rodean las puntas y rompen según la profundidad. La espuma aparece donde la imagen satelital muestra rompientes.
- **Nubes:** volumétricas, con un campo orográfico calculado con la teoría de ondas de montaña (Smith 2004), que forma bancos sobre el cordón montañoso. Sus sombras caen sobre el terreno y el mar.
- **Vegetación:** el mapa de cobertura decide dónde hay bosque. Cerca de la cámara se dibujan árboles con malla completa y, a media distancia, *impostores* hemi-octaédricos: en la vista de Cumberland son más de 80 mil. Tienen sombras en cascada y viento.
- **Imagen final:** exposición automática como un fotómetro de cámara, curva tonal fotográfica, balance de blancos y resolución dinámica para sostener los cuadros por segundo.

[Image: El archipiélago visto desde lo alto, con nubes volumétricas sobre el mar]

*Vista general del archipiélago: Robinson Crusoe, Santa Clara al suroeste y nubes volumétricas con sus sombras sobre el océano.*

[Image: Olas rompiendo contra los acantilados de Bahía Chupones]

*Rompiente en Bahía Chupones: las olas se refractan sobre la batimetría real y rompen según la profundidad.*

[Image: Acantilados de Punta Isla, en el extremo suroeste de la isla]

*Los acantilados de Punta Isla, en el suroeste: el relieve sale del modelo de elevación real, afinado donde las imágenes lo permiten.*

[Image: Bosque nativo en las laderas bajo El Yunque]

*Bosque nativo bajo El Yunque: árboles con malla completa cerca de la cámara e impostores a media distancia, ubicados según el mapa de cobertura.*

## Rendimiento

El objetivo era 60 fps en un MacBook Air M3, que no tiene ventilador y se calienta con las cargas sostenidas. Aprendí a medir antes de optimizar: en ANGLE/Metal, los *timer queries* de GPU dan intervalos que se superponen y no sirven. Por eso el arnés de pruebas mide el costo de cada parte **por eliminación**, apagándolas una a una dentro de la misma página. En Bahía Cumberland, a 1600×900, un cuadro completo toma unos 15 ms. La resolución dinámica y las texturas ASTC cubren el resto.

## Lo que aprendí

- **Un contrato de datos explícito vale oro.** Con la especificación como única fuente de verdad, el pipeline y el renderer pudieron evolucionar por separado sin romperse.
- **La física da realismo gratis.** La refracción de las olas, la atmósfera o las nubes sobre el relieve calculadas con su física real se ven bien en situaciones que nunca probé a mano.
- **Desarrollo asistido por IA.** Construí el proyecto con Claude Code como copiloto. Para mí lo clave fue escribir especificaciones precisas y verificar cada resultado con capturas y mediciones, en vez de confiar en la primera respuesta.

Hoy el proyecto corre en local: parte de las imágenes satelitales que usa (Esri World Imagery) tiene licencia de uso personal, así que publicarlo en abierto requeriría reemplazarlas.

## Stack

- **Renderer:** TypeScript, three.js, WebGL2, Web Workers, Vite
- **Pipeline de datos:** Python, NumPy, SciPy, scikit-image, rasterio
- **Datos:** Copernicus DEM GLO-30, Sentinel-2 L2A, Esri World Imagery, GMRT, GEBCO, OpenStreetMap
- **Herramientas:** Playwright (arnés de capturas y rendimiento), Claude Code

---

*Imágenes renderizadas en tiempo real por el proyecto. Imágenes satelitales: Esri World Imagery (Esri, Maxar, Earthstar Geographics y la comunidad de usuarios GIS); contiene datos modificados de Copernicus Sentinel-2; Copernicus DEM GLO-30 © DLR e.V. 2010–2014 y © Airbus Defence and Space GmbH 2014–2018; batimetría GMRT y GEBCO; © colaboradores de OpenStreetMap.*
