Three.js modelado programático de ciudades en 3D [OpenStreetMap]

Para mi proyecto de investigación en Howest, decidí construir una versión 3D del proyecto "Rutas de mapa animadas para narración interactiva" de Lucas Bebber.

Usaré siluetas vectoriales de OSM para extruir las formas de los edificios y agregarlas a la escena 3js que luego animaré.

inserte la descripción de la imagen aquí

Recomendación: use el editor NSDT para crear rápidamente escenas 3D programables

1. Configuración del entorno de desarrollo

Para trabajar con paquetes Node y npm, elegí usar Vite.js. Vite es una herramienta de compilación diseñada para proporcionar una experiencia de desarrollo más rápida y ágil para proyectos web modernos. Consta de dos partes principales:

  • El servidor de desarrollo proporciona numerosas mejoras de funciones sobre los módulos ES nativos, como el reemplazo de módulo activo (HMR) extremadamente rápido.
  • Un comando de compilación que agrupa el código con Rollup, preconfigurado para generar activos estáticos altamente optimizados para la producción.

Elegí Vite porque lo he usado en el pasado para algunos proyectos de Vue.js, por lo que estoy familiarizado con él y ha demostrado ser rápido y confiable.

Three.js fue elegido como el marco elegido para este proyecto debido a su popularidad, lo que ha resultado en una extensa documentación y tutoriales.

Como quiero poder integrar este proyecto de investigación en mi propio sitio web en el futuro, decidí desarrollarlo como un paquete NPM. Esto implicó hacer dos proyectos separados: el primero para la aplicación 3D real y el otro para un sitio web de prueba que implementa la aplicación.

En la carpeta del proyecto, el comando npm init se usa para crear el archivo package.json, que contiene metadatos para el paquete, como el nombre, la versión, las dependencias, los puntos de entrada y otra información. Index.js servirá como punto de entrada para el paquete, con la carpeta src que contiene el código y la carpeta de ejemplos que contiene los recursos predeterminados.

El plan es dividir la funcionalidad en módulos de JavaScript separados para mayor claridad y facilidad de mantenimiento, y finalmente se inicializarán, globales, ciudades, animaciones y rutas.

2. Inicializar el módulo

Comience por inicializar el módulo. La función initialize() crea y configura objetos de escena, cámara, luz y renderizador, selecciona un elemento de lienzo del DOM y le adjunta el renderizador. Además, se usa para habilitar o deshabilitar la información de depuración, como el contador de FPS y el visualizador Axis.

Luego, este módulo se usa para inicializar MapControls y crear bucles de animación, pero consulte la sección Interactividad para obtener más información.

Este paquete npm se llama Storymap

3. Crea una página de gestión

Para probar si el paquete npm recién creado funciona, se usa npm create vite@latest.

Inicializa un proyecto de demostración independiente que actuará como consumidor del paquete.

Para instalar el paquete local en este proyecto, se crea un enlace simbólico a Storymap en los node_modules de la demostración mediante el enlace npm.

Este proyecto se usará para crear y probar un panel de administración donde los desarrolladores web pueden crear rutas para recorridos nuevos o actualizados.

Después de crear el elemento canvas en el archivo index.html y agregarlo a la configuración del storymap, la prueba se puede iniciar escribiendo npm run dev en la terminal y yendo a localhost:3000 en el navegador, donde vemos un Tres vacío escena .js.

Dado que el paquete del storymap tiene enlaces simbólicos, los cambios que le hagamos se propagarán automáticamente al proyecto de demostración, por lo que puede mantener el proyecto de demostración en ejecución y seguir escribiendo código y probando cambios, lo que permite un proceso de desarrollo eficiente.

4. Datos OSM

Originalmente planeé usar OSM Buildings para obtener los datos, pero descubrí que su documentación estaba desactualizada y decidí cambiar a Overpass Turbo para poder comenzar a desarrollar más rápido y descubrir cómo usar OSMB. OSMB usa GeoJSON y Overpass Turbo permite exportar sus datos al formato JSON.

Los archivos GeoJSON contienen varios elementos del mapa, como carreteras, parques, edificios representados por varias coordenadas y matrices de metadatos. Los edificios tienen perfiles, vanos, secciones, alturas o niveles, y muchas otras propiedades.
inserte la descripción de la imagen aquí

Un modelo de ejemplo de un edificio y sus metadatos

JSON es una buena opción porque su popularidad en la web significa soporte nativo para analizarlo. Descubrir cómo usar la API de OverPass tomó algún tiempo, ya que la documentación no aclara cómo estructurar su lenguaje de consulta, y su uso aún puede ser un desafío. Puede ver la consulta final a continuación.

[out:json]
[bbox:{
   
   {bbox}}]
[timeout:30];

(
way["building"]({
   
   {bbox}});
relation["building"]["type"="multipolygon"]({
   
   {bbox}});

way["highway"]({
   
   {bbox}});
relation["highway"]["type"="polygon"]({
   
   {bbox}});

way["natural"="water"]({
   
   {bbox}});
way["water"="lake"]({
   
   {bbox}});
way["natural"="coastline"]({
   
   {bbox}});
way["waterway"="riverbank"]({
   
   {bbox}});

way["leisure"="park"]({
   
   {bbox}});
way["leisure"="garden"]({
   
   {bbox}});
);

out;
>;
out qt;

5. Representación arquitectónica de OSM

La generación de edificios se divide en módulos de ciudad. Analiza archivos GeoJson para encontrar edificios. Los edificios se representan como conjuntos de coordenadas de latitud y longitud de polígonos que forman el contorno del edificio, eventualmente más polígonos que representan agujeros en esa forma, como un jardín interior o un voladizo.

El primer gran desafío lo provoca el propio JavaScript y el sistema de coordenadas de Three.js. El centro de la escena está representado por 0,0,0, y cuanto más lejos de él, menos preciso e inestable es, principalmente debido a la poca precisión de punto flotante de JavaScript.

Las coordenadas de GeoJSON son coordenadas globales de latitud y longitud, y son grandes flotantes con pequeñas diferencias, lo que exacerba el problema de precisión.

Por lo tanto, es necesario normalizar las coordenadas globales al espacio local. Ocupa el centro de la región, utilizando una biblioteca de terceros llamada geolib

Calcula la distancia desde el centro hasta cada punto, lo que da como resultado coordenadas normalizadas al origen, con una desviación estándar mayor.
inserte la descripción de la imagen aquí

cuadrícula que muestra las coordenadas relativas al origen

Con estos datos normalizados, puedo crear formas y geometrías de Three.js. Usando la funcionalidad incorporada de Three.js, pude "perforar" fácilmente agujeros fuera de los contornos. Luego, esta geometría se usa junto con los materiales para crear una malla, que luego se agrega a la escena.

Debido a la orientación de three.jsXYZ, también tengo que rotarlo 90 y 180 grados en los ejes X y Z respectivamente. Usando este método, ahora tengo una primera versión muy plana de la ciudad. Para hacerlo 3D, utilicé la propiedad de nivel, que representa el número de pisos multiplicado por un valor arbitrario para extruir estas formas.

inserte la descripción de la imagen aquí

¡Ciudades en tu navegador!

6. Actuación e interacción

Ahora que se generan las ciudades, surge un problema: el rendimiento apesta, los FPS se reducen a un solo dígito. La solución es combinar todos los edificios en una gran malla en lugar de generar objetos separados para cada edificio. Este enfoque tiene el efecto secundario de que todos los edificios tendrán el mismo material, pero vale la pena debido al aumento masivo del rendimiento. Esta solución se aplica a toda la geometría generada posteriormente, como carreteras, espacios verdes y vías fluviales.

Los desarrolladores web deben poder cargar y navegar por áreas enteras y establecer puntos de referencia para las rutas de los recorridos. Esto se puede lograr usando el control de mapa three.js. Es un subconjunto del control Orbit de 3js que funciona de manera similar a Google Maps y otro software popular de mapas digitales, lo que permite hacer clic con el botón derecho para rotar la cámara alrededor del centro de la página y hacer clic con el botón izquierdo para arrastrar la cámara. Se importan e inicializan en el módulo de inicialización. Para que funcionen, deben actualizar cada cuadro, por lo que se agregan al bucle de animación.

Para crear waypoints para una ruta, debe seleccionar la carretera y un método para convertir las coordenadas del mouse 2D en un espacio 3D. Para este último, se puede usar un emisor de rayos para proyectar un rayo desde la posición del mouse hacia la escena 3D y ver lo que se cruza. Desde allí, puede filtrarlo para seleccionar solo carreteras.

Para los primeros, se utilizan técnicas similares para los edificios. Usar Overpass Turbo en lugar de OSM Buildings resultó ser una bendición disfrazada, dado que la API de OSM solo proporciona GeoJSON para edificios, necesitaba encontrar e implementar una API adicional para obtener datos de carreteras.

Las consultas de la API de paso elevado se ajustan para incluir carreteras, sus coordenadas se normalizan, se generan y se agregan a la escena en el módulo de la ciudad. Con la ciudad luciendo monótona, OverPass también se está utilizando para capturar datos sobre espacios verdes y vías fluviales.
inserte la descripción de la imagen aquí

el camino que se dibuja

El módulo de ruta se utiliza para implementar Raycaster y otros controles necesarios para dibujar y exportar rutas. Three.js tiene incorporado Raycaster, por lo que es fácil de implementar. Está vinculado a un detector de eventos OnMouseMove que emite continuamente un rayo para mostrar si el usuario ha seleccionado una ruta.

Los clics con el botón izquierdo del mouse se utilizan para establecer puntos de ruta, con varios botones del teclado para funciones de guardar, restablecer, deshacer y rehacer.

Las rutas se exportan a una matriz JSON. Una vez que el desarrollador comienza a crear una ruta, necesito que siga el mouse. Entonces, antes de hacer clic, el último punto de la ruta se vincula a la posición del mouse, también usando el detector de eventos OnMouseMove.

7. Cliente

Para probar la implementación del cliente, creé un nuevo proyecto y lo configuré de manera similar al proyecto de administración, usando vite para iniciar el proyecto y usando el enlace npm para instalar el paquete Storymap. El único cambio es que, en lugar de ocupar toda la página esta vez, la escena ocupa la mitad y la otra mitad se usa para mostrar información sobre puntos de interés.

Para el cliente, su único control sobre la interacción es desplazarse en el navegador. No deberían poder interactuar directamente con la escena Three.js, al igual que el usuario no pudo interactuar con el lienzo en la demostración de Lucas.

En resumen, a medida que el cliente se desplaza hacia abajo en el navegador, la ruta debe aparecer y animarse desde la posición de inicio y finalizar cuando el usuario se desplaza hacia abajo y la cámara lo sigue.

Esta fue una de las partes más difíciles del proyecto.

inserte la descripción de la imagen aquí

Diagrama que muestra cómo se calculan las rutas

En resumen, una ruta se dibuja como un objeto Línea con varios puntos. A medida que el usuario se desplaza hacia abajo, el último punto sigue actualizándose, lo que hace que parezca que está animado.

Para obtener las coordenadas del punto, la función getScrollPercentage() se adjunta al detector de eventos de desplazamiento y se utiliza para calcular la posición de desplazamiento del navegador. Luego use ese porcentaje para manipular continuamente qué par de puntos elegir como inicio y final, y use la función lerpVectors para interpolar entre los dos y calcular cuál debería ser el último punto de la ruta actual.

Aclaremos esto con un ejemplo. Elijamos un camino con 11 puntos individuales. El primero se dibuja desde cero, por lo que debe dividir la altura total de la página (en porcentaje) por 10.

  • Esto quiere decir que por cada 10% que hacemos scroll, se pasa un punto. El porcentaje de desplazamiento global determina entre qué par de puntos necesitamos interpolar.
  • Si el usuario se desplaza al 26,53 %, necesitamos usar la interpolación para calcular las coordenadas del punto que es el 65,3 % entre los puntos 3 y 4.
  • Esta coordenada se utiliza para actualizar la última posición de la Ruta. Esto sucede cada vez que el usuario se desplaza, dando la ilusión de que la ruta se está reduciendo o creciendo.
  • La posición de la cámara se desplaza hacia la parte posterior de un lado de la ruta, y la última posición de la ruta se utiliza como objetivo, que se mueve con la ruta.

inserte la descripción de la imagen aquí

estructura del proyecto

El resultado final de la investigación consistió en 3 proyectos distintos: el paquete Storymap npm responsable de generar y analizar los datos del mapa, el proyecto de gestión que permite la creación de rutas en el mapa y el cliente.

El proyecto anima los caminos creados a través de la ciudad.
inserte la descripción de la imagen aquí

Puede hacer clic aquí para ver la demostración en línea y el código fuente está disponible en GitHub .


Enlace original: OSM+tres.js para crear una ciudad 3D—BimAnt

Supongo que te gusta

Origin blog.csdn.net/shebao3333/article/details/132384145
Recomendado
Clasificación