Simulación de erosión superficial basada en partículas


Nota: el código fuente completo del proyecto, así como las explicaciones sobre su uso y lectura, se pueden encontrar en Github [ aquí ].

Me tomé un descanso de mi tesis de maestría para trabajar en lo que había estado posponiendo durante mucho tiempo: la generación mejorada del terreno para mi proyecto Territorio . Una forma sencilla de implementarlo es la erosión hidráulica, ¡por eso lo creé!

Para un rompecabezas de software de un día, funcionó bastante bien y resultó no ser tan complicado como esperaba. Los resultados se generan rápidamente, tienen un significado físico y se ven increíbles.

En este artículo, hablaré sobre mi implementación simple en C ++ de un sistema de erosión hidráulica de malla cuadrada a base de partículas. Explicaré todas las justificaciones físicas que subyacen a la implementación y hablaré sobre las matemáticas. El código es extremadamente simple (solo unas 20 líneas para las matemáticas de la erosión) y rápido de implementar, por lo que lo recomiendo a cualquiera que quiera aumentar el realismo de su terreno.

Los resultados se representan usando una versión reducida de mi motor Homebrew OpenGl Engine , que modifiqué para representar una matriz 2D de puntos como un mapa de altura. Una versión simplificada del motor es mucho más fácil de entender si está interesado en aprender OpenGL en C ++.

Implementación


Inspirado por muchas fuentes de erosión hidráulica, decidí que sería más lógico usar partículas.

La erosión basada en partículas es muy simple:

  • Creamos una partícula en un punto aleatorio en la superficie.
  • Se mueve / desliza a lo largo de la superficie usando mecánica clásica estándar (hablaremos de ellos a continuación)
  • Realizamos la transferencia de materia / sedimento entre la superficie y la partícula (esto también se explica a continuación)
  • Evaporamos parte de la partícula
  • Si la partícula está fuera del mapa o es demasiado pequeña, destrúyala
  • Repita el proceso con el número deseado de partículas.

Nota: los parámetros del sistema se explican en la sección correspondiente. El más importante es el factor de tiempo dt, que escala proporcionalmente todos los parámetros. Esto nos permite aumentar la frecuencia de la simulación (a costa de aumentar el ruido) sin cambiar la escala relativa de los parámetros. Esto se puede ver en el siguiente código.

Partículas


Para hacer esto, creé una estructura de partículas simple que contiene todas las propiedades que necesito:

struct Particle{
  //Construct particle at position _pos
  Particle(glm::vec2 _pos){ pos = _pos; }

  glm::vec2 pos;
  glm::vec2 speed = glm::vec2(0.0); //Initialize to 0

  float volume = 1.0;   //Total particle volume
  float sediment = 0.0; //Fraction of volume that is sediment!
};

Nota: en caso de que no esté familiarizado con la biblioteca GLM: la uso para realizar operaciones vectoriales.

Una partícula tiene una posición y velocidad que determina cómo se mueve. Además, tiene un volumen y una fracción que determina cuánto del volumen son rocas sedimentarias.

Movimiento: mecánica clásica.


El movimiento de partículas en la superficie se simula utilizando la mecánica clásica . En resumen, la posición x de la partícula cambia por la velocidad v , cambia por la aceleración a .



Nota: las letras en negrita indican que el valor es un vector.

También sabemos que la fuerza es igual a la masa por la aceleración:


La partícula experimenta una aceleración hacia abajo causada por la gravedad, pero está en la superficie, haciendo imposible la aceleración hacia abajo. Por lo tanto, en lugar de esto, la partícula se somete a una fuerza F dirigida a lo largo de la superficie y proporcional a la normal a la superficie.

Por lo tanto, podemos decir que la aceleración a es proporcional al vector normal de la superficie n dividido por la masa de la partícula.


donde k es la constante de proporcionalidad ym es la masa de partículas. Si la masa es igual al volumen multiplicado por la densidad, entonces obtenemos el sistema completo de movimiento de partículas usando la mecánica clásica:

//... particle "drop" was spawned above at random position

glm::ivec2 ipos = drop.pos; //Floored Droplet Initial Position
glm::vec3 n = surfaceNormal(ipos.x, ipos.y);  //Surface Normal

//Accelerate particle using classical mechanics
drop.speed += dt*glm::vec2(n.x, n.z)/(drop.volume*density);
drop.pos   += dt*drop.speed;
drop.speed *= (1.0-dt*friction);  //Friction Factor

//...

Nota: la velocidad después del movimiento de partículas se reduce por el vector de fricción. Tenga en cuenta que el factor de paso de tiempo se incluye aquí. Las partículas tienen su propia inercia, proporcional a su densidad debido al hecho de que simulamos su movimiento usando la aceleración.

El proceso de formación de rocas sedimentarias: transferencia de masa


El proceso de formación de rocas sedimentarias ocurre físicamente como la transferencia de rocas sedimentarias desde la tierra a la partícula y viceversa en el punto donde se encuentra la partícula (" transferencia de masa ").

En la tecnología química, la transferencia de masa (es decir, el cambio en la masa / sedimento a lo largo del tiempo) entre dos fases (en este caso, la superficie de la tierra y una caída) generalmente se describe utilizando coeficientes de transferencia de masa .

La transferencia de masa es proporcional a la diferencia entre la concentración c y la concentración de equilibrio c_eq:


donde k es la constante de proporcionalidad (coeficiente de transferencia de masa). Esta diferencia entre el equilibrio y la concentración real a menudo se llama la "fuerza impulsora".

Si la concentración de equilibrio es mayor que la corriente, entonces la partícula absorbe rocas sedimentarias. Si es inferior, los pierde. Si son iguales, entonces no ocurrirán cambios.

El coeficiente de transferencia de masa se puede interpretar de varias maneras:

  • Como la frecuencia de la transición entre fases (aquí es "tasa de deposición")
  • La velocidad a la que la concentración de gotas tiende a la concentración de equilibrio

El sistema depende completamente de determinar la concentración de equilibrio. Dependiendo de la definición, el sistema demostrará diferentes dinámicas de deposición de rocas sedimentarias. En mi implementación, la concentración de equilibrio es mayor si nos movemos hacia abajo, y si nos movemos más rápido, y también es proporcional al volumen de la partícula:

//...

//Compute Equilibrium Sediment Content
float c_eq = drop.volume*glm::length(drop.speed)*(heightmap[ipos.x][ipos.y]-heightmap[(int)drop.pos.x][(int)drop.pos.y]);

if(c_eq < 0.0) c_eq = 0.0;

//Compute Capacity Difference ("Driving Force")
float cdiff = c_eq - drop.sediment;

//Perform the Mass Transfer!
drop.sediment += dt*depositionRate*cdiff;
heightmap[ipos.x][ipos.y] -= dt*drop.volume*depositionRate*cdiff;

//...

Nota: el cambio en la concentración dentro de la partícula se describe completamente mediante la ecuación de transferencia de masa. El cambio en el mapa de altura se multiplica adicionalmente por el volumen de partículas, porque lo cambiamos proporcionalmente no a la concentración, sino a la masa (la concentración se multiplica por el volumen).

Otros aspectos


El mapa de altura se inicializa mediante un ruido Perlin de varias capas con una semilla aleatoria.

Al final de cada paso de tiempo, la partícula pierde un poco de masa de acuerdo con la velocidad de evaporación:

//...

drop.volume *= (1.0-dt*evapRate);

//...

Este proceso se repite para miles de partículas creadas en lugares aleatorios y simuladas por separado (en mi caso, los cálculos se realizan secuencialmente en la CPU).

Mi implementación predeterminada incluye un buen conjunto de parámetros. Después de 200 mil partículas, la erosión se ve muy bien.

resultados


El código terminado para el proceso de erosión es de aproximadamente 20 líneas sin comentarios.

Aquí hay una comparación de "antes y después" para 10 muestras. La simulación genera crestas muy hermosas en las elevaciones, las rocas sedimentarias se depositan a los lados de algunas crestas, lo que conduce a la creación de hermosas mesetas.


Una selección de diez comparaciones de antes y después. El sombreador que escribí recibe dos colores en la entrada. También implementé mapeo de sombras, niebla dependiente de la distancia y sombreado Phong.

Aquí hay diez muestras más (diferentes) solo con los resultados:


Diez resultados de muestra más. Difieren de las anteriores, incluso si algunas formaciones se parecen.

Al elegir la semilla de manera diferente durante la inicialización del mapa, puede crear resultados controlados diferentes.

Tiempo de simulación


El tiempo de simulación está directamente relacionado con la vida útil de las partículas y el número de partículas simuladas.

Varios factores afectan la vida de las partículas, y puede variar mucho para cada partícula, porque se crean en lugares aleatorios:

  • Fricción e inercia: velocidad de partículas
  • Tamaño de cuadrícula: la probabilidad de que las partículas se caigan del mapa.
  • Velocidad de evaporación: velocidad de extinción de partículas

Con los parámetros predeterminados, una simulación de una partícula individual requiere 10-100 milisegundos, lo que resulta en 10-20 segundos para una simulación de 200,000 partículas.

Puede aumentar el grado de erosión y reducir la vida útil de las partículas aumentando el intervalo de tiempo sin cambiar el número de partículas simuladas. Esto puede hacer que la simulación sea más "ruidosa", y si el intervalo de tiempo es demasiado grande, existe la posibilidad de que la simulación falle.

La simulación en sí misma en el motor incurre en costos adicionales para volver a crear la malla de superficie para el renderizado.

Puede optimizar el código reduciendo el costo de volver a crear la malla o aumentando la velocidad de un paso de tiempo para una partícula individual.

Trabajar para el futuro


Pronto insertaré este código en mi proyecto de Territorio como base para generar un mapa de altura en el generador de elevación.

Ya escribí un marco para la simulación simplificada de la dinámica de fluidos en los sistemas climáticos, pero aún no está listo para su publicación. Este sistema obtiene patrones climáticos de un mapa de altura. ¡Este será el tema para una publicación futura sobre sistemas climáticos procesales físicamente precisos!

Usando un sistema climático simulado, será posible en el futuro tomar muestras de partículas de la distribución (por ejemplo, en lugares donde llueve) en lugar de distribuirlas de manera uniforme en el mapa. Se combinarán idealmente con la geología y el clima del relieve.

Además, después de agregar diferentes tipos de formaciones geológicas, las piedras pueden tener un grado diferente de solubilidad. Esto afectará directamente el coeficiente de transferencia de masa (tasa de deposición), creando una tasa de erosión diferente.

Este sistema no puede simular un flujo real de fluidos y ríos, pero puede adaptarse a tales tareas. Lo pensaré y tal vez en el futuro lanzaré una continuación de la publicación.

All Articles