Mapas 3D, y sus aplicaciones
Generar mapas en 3D de cualquier parte del mundo… para que sirve?
Bueno, a parte ser visualmente impactante, puede ser útil en algunos campos cómo arquitectura, aplicaciones de rastreo y videojuegos.
De hecho esa última es la razón principal del módulo (addon) que he creado en Godot 3.0.
Como funciona
Primero he decidido utilizar lo que MapBox pone a disposición: imágenes satelitales y mapas de elevación.
La API es fácil de utilizar y solo necesitas crear una cuenta y obtener el token.
El proceso de generación de los mapas utiliza los heightmap para dibujar el modelo 3D, y una imagen satelital de la misma área para el color de cada polígono dibujado.
Próximamente planeo añadir texture y normals (que permitiría aumentar el nivel de detalle, o reducir el número de poligonos sin perder cualidad), también de ser posible LOD automático
Mapas de elevación
Las mapas de elevación utilizan imágenes cuya información de elevación es codificada en sus colores:
Cada componente de color (rojo, verde y azul) corresponde a una cantidad (en metros). Y eso, según la documentación oficial, se decodifica con esta fórmula:
var altitude = -10000 + ((r * 256 * 256 + g * 256 + b) * 0.1)
Así que es de notar que las mapas de elevación, no son una falsa coloración que nos indica la elevación.
Por eso, de la imagen de elevación, no siempre se puede entender la forma del objeto.
En mi implementación cada pixel corresponde a un quad, o 2 triangulos que toman como elevacíon la del pixel analizado y de sus pixeles contiguos, de esta forma:
var bottomleft = Vector3(i * dist - half_size, ht[i][j]["height"] * altitude_multiplier, j * dist - half_size) var bl_color = ht[i][j]["color"] var upperleft = Vector3(i * dist - half_size, ht[i][j + 1]["height"] * altitude_multiplier, (j + 1) * dist - half_size) var ul_color = bl_color.linear_interpolate(ht[i][j + 1]["color"], 0.5) var upperright = Vector3((i + 1) * dist - half_size, ht[i + 1][j + 1]["height"] * altitude_multiplier, (j + 1) * dist - half_size) var ur_color = bl_color.linear_interpolate(ht[i+1][j+1]["color"], 0.5) var bottomright = Vector3((i + 1) * dist - half_size, ht[i + 1][j]["height"] * altitude_multiplier, (j) * dist - half_size) var br_color = bl_color.linear_interpolate(ht[i+1][j]["color"], 0.5) surf_tool.add_color(bl_color) surf_tool.add_vertex(bottomleft) surf_tool.add_color(br_color) surf_tool.add_vertex(bottomright) surf_tool.add_color(ul_color) surf_tool.add_vertex(upperleft) surf_tool.add_color(br_color) surf_tool.add_vertex(bottomright) surf_tool.add_color(ur_color) surf_tool.add_vertex(upperright) surf_tool.add_color(ul_color) surf_tool.add_vertex(upperleft)
Es de notar que al momento de renderizar los colores no son exactamente los mismos de la texture, eso debido a la interplación de colores, aspecto que mejoraré en las próximas versiones.
Imágen Satelital (texture)
La imágen satelital, o texture, es utilizada para dar un color por cada triangulo dibujado en 3D.
Así que cáda vertex que se añade toma el color del pixel correspondiente.
Obtener los datos desde MapBox
Otro elemento importante de esta implementación es el servicio que MapBox pone a disposición (la verdad, he buscado entre los datos que la NASA pone a disposición, pero no encontré una respuesta rápida, y decidí ir por la ruta más fácil, ya que estoy familiarizado con las APIs de MapBox).
Simplemente decodificando las coordenadas en el tile correspondiente se obtienen las imágenes de elevación y satelitales. Aproveché de esta necesidad (de decodificar latitud, longitud y zoom en el tile correspondiente), para crear una pequeña librería que me permite esta y otras decodificaciones.
Resultado final y problemas técnicos encontrados
Bueno, cómo siempre se dice, una imagen dice mas que mil palabras, así que aquí pueden ver el resultado final.
Cabe destacar que debido a problemas no resueltos, cada imagen resulta en el rendering de 4 o hasta 16 tiles, o mesh, más pequeñas, lo que resulta en el desalineamento, en ciertos casos, de los tiles contiguos, cómo se puede notar en la animación.