Simulação de erosão de superfície baseada em partículas


Nota: o código fonte completo do projeto, bem como explicações sobre seu uso e leitura, podem ser encontrados no Github [ aqui ].

Fiz uma pausa na tese de mestrado para trabalhar no que vinha adiando há muito tempo: melhor geração de terrenos para o meu projeto Território . Uma maneira simples de implementá-lo é a erosão hidráulica, e foi por isso que eu o criei!

Para um quebra-cabeça de software de um dia, funcionou muito bem e acabou não sendo tão complicado quanto eu esperava. Os resultados são gerados rapidamente, têm um significado físico e têm uma aparência incrível.

Neste artigo, falarei sobre minha implementação simples em C ++ de um sistema de erosão hidráulica de malha quadrada baseada em partículas. Vou explicar todas as justificativas físicas subjacentes à implementação e falar sobre matemática. O código é extremamente simples (apenas cerca de 20 linhas para a matemática da erosão) e rápido de implementar, então eu o recomendo para quem quiser aumentar o realismo de seu alívio.

Os resultados são renderizados usando uma versão simplificada do meu Homebrew OpenGl Engine , que eu modifiquei para renderizar uma matriz 2D de pontos como um mapa de altura. Uma versão simplificada do mecanismo é muito mais fácil de entender se você estiver interessado em aprender OpenGL em C ++.

Implementação


Inspirado por muitas fontes de erosão hidráulica, decidi que seria mais lógico usar partículas.

A erosão baseada em partículas é muito simples:

  • Criamos uma partícula em um ponto aleatório na superfície.
  • Ele se move / desliza pela superfície usando a mecânica clássica padrão (falaremos sobre eles abaixo)
  • Realizamos a transferência de matéria / sedimento entre a superfície e a partícula (isso também é explicado abaixo)
  • Evaporaremos parte da partícula
  • Se a partícula estiver fora do mapa ou for muito pequena, destrua-a
  • Repita o processo com o número desejado de partículas.

Nota: os parâmetros do sistema são explicados na seção correspondente. O mais importante é o fator de etapa do tempo dt, que escala proporcionalmente todos os parâmetros. Isso nos permite aumentar a frequência da simulação (ao custo de aumentar o ruído) sem alterar a escala relativa dos parâmetros. Isso pode ser visto no código abaixo.

Partículas


Para fazer isso, criei uma estrutura de partículas simples que contém todas as propriedades necessárias:

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: caso você não esteja familiarizado com a biblioteca GLM: eu a uso para executar operações de vetor.

Uma partícula tem uma posição e velocidade que determina como se move. Além disso, possui um volume e uma fração que determina quanto do volume são rochas sedimentares.

Movimento: mecânica clássica


O movimento de partículas na superfície é simulado usando a mecânica clássica . Em resumo, a posição x da partícula é alterada pela velocidade v , alterada pela aceleração a .



Nota: letras em negrito indicam que o valor é um vetor.

Também sabemos que a força é igual à massa vezes a aceleração:


A partícula experimenta aceleração descendente causada pela gravidade, mas está na superfície, o que torna impossível a aceleração descendente. Portanto, em vez disso, a partícula é submetida a uma força F direcionada ao longo da superfície e proporcional ao normal à superfície.

Portanto, podemos dizer que a aceleração a é proporcional ao vetor normal da superfície n dividido pela massa da partícula.


onde k é a constante de proporcionalidade e m é a massa de partículas. Se a massa é igual ao volume multiplicado pela densidade, obtemos o sistema completo de movimento de partículas usando a mecânica clássica:

//... 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: a velocidade após o movimento das partículas é reduzida pelo vetor de atrito. Observe que o fator de etapa do tempo está incluído aqui. As partículas têm sua própria inércia, proporcional à sua densidade, devido ao fato de simularmos seus movimentos usando aceleração.

O processo de formação de rochas sedimentares: transferência de massa


O processo de formação de rochas sedimentares ocorre fisicamente como a transferência de rochas sedimentares da terra para a partícula e de volta ao ponto da partícula (" transferência de massa ").

Na tecnologia química, a transferência de massa (isto é, a mudança de massa / sedimento ao longo do tempo) entre duas fases (neste caso, a superfície da Terra e uma gota) é geralmente descrita usando coeficientes de transferência de massa .

A transferência de massa é proporcional à diferença entre a concentração ce concentração de equilíbrio c_eq:


onde k é a constante de proporcionalidade (coeficiente de transferência de massa). Essa diferença entre equilíbrio e concentração real é freqüentemente chamada de "força motriz".

Se a concentração de equilíbrio for maior que a corrente, a partícula absorve as rochas sedimentares. Se menor, então os perde. Se forem iguais, nenhuma alteração ocorrerá.

O coeficiente de transferência de massa pode ser interpretado de várias maneiras:

  • Como a frequência da transição entre fases (aqui é a "taxa de deposição")
  • A taxa na qual a concentração de gotículas tende à concentração de equilíbrio

O sistema é completamente dependente da determinação da concentração de equilíbrio. Dependendo da definição, o sistema demonstrará diferentes dinâmicas de deposição de rochas sedimentares. Na minha implementação, a concentração de equilíbrio é maior se descermos e se movermos mais rápido, e também é proporcional ao volume da 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: a mudança na concentração dentro da partícula é completamente descrita pela equação de transferência de massa. A alteração no mapa de altura é adicionalmente multiplicada pelo volume de partículas, porque o alteramos proporcionalmente não à concentração, mas à massa (a concentração é multiplicada pelo volume).

Outros aspectos


O mapa de altura é inicializado por um ruído Perlin de várias camadas com uma semente aleatória.

No final de cada etapa, a partícula perde um pouco de massa de acordo com a taxa de evaporação:

//...

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

//...

Esse processo é repetido para milhares de partículas criadas em locais aleatórios e simuladas separadamente (no meu caso, os cálculos são realizados seqüencialmente na CPU).

Minha implementação padrão inclui um bom conjunto de parâmetros. Após 200 mil partículas, a erosão parece muito boa.

resultados


O código final para o processo de erosão é de aproximadamente 20 linhas sem comentários.

Aqui está uma comparação "antes e depois" para 10 amostras. A simulação gera cumes muito bonitos em elevações, rochas sedimentares são depositadas nas laterais de alguns cumes, o que leva à criação de belos planaltos.


Uma seleção de dez comparações antes e depois. O sombreador que escrevi recebe duas cores na entrada. Também implementei mapeamento de sombra, neblina dependente da distância e sombreamento Phong.

Aqui estão mais dez amostras (diferentes) apenas com os resultados:


Mais dez resultados de amostra. Eles diferem dos anteriores, mesmo que algumas formações pareçam semelhantes.

Ao escolher a semente de maneira diferente durante a inicialização do mapa, você pode criar resultados diferentes controlados.

Tempo de simulação


O tempo de simulação está diretamente relacionado ao tempo de vida das partículas e ao número de partículas simuladas.

Vários fatores afetam a vida útil das partículas e podem variar bastante para cada partícula, porque são criados em locais aleatórios:

  • Atrito e inércia: velocidade das partículas
  • Tamanho da grade: a probabilidade de partículas caírem do mapa.
  • Taxa de evaporação: taxa de extinção de partículas

Com os parâmetros padrão, uma simulação de uma partícula individual requer 10 a 100 milissegundos, o que resulta em 10 a 20 segundos para uma simulação de 200.000 partículas.

Você pode aumentar o grau de erosão e reduzir a vida útil das partículas aumentando o intervalo de tempo sem alterar o número de partículas simuladas. Isso pode tornar a simulação mais "barulhenta" e, se o tempo for muito grande, há uma chance de a simulação falhar.

A própria simulação no mecanismo incorre em custos adicionais para recriar a malha de superfície para renderização.

Você pode otimizar o código reduzindo o custo de recriar a malha ou aumentando a velocidade de uma etapa para uma partícula individual.

Trabalhe para o futuro


Em breve, inserirei esse código no meu projeto Territory como base para gerar um mapa de altura no gerador de elevação.

Eu já escrevi uma estrutura para simulação simplificada da dinâmica de fluidos em sistemas climáticos, mas ainda não está pronta para publicação. Este sistema obtém padrões climáticos a partir de um mapa de altura. Este será o tópico para uma publicação futura em sistemas climáticos processuais fisicamente precisos!

Usando um sistema climático simulado, será possível, no futuro, coletar amostras da distribuição (por exemplo, em locais onde chove), em vez de distribuí-las uniformemente no mapa. Serão idealmente combinados com a geologia e o clima do relevo.

Além disso, depois de adicionar diferentes tipos de formações geológicas, as pedras podem ter um grau diferente de solubilidade. Isso afetará diretamente o coeficiente de transferência de massa (taxa de deposição), criando uma taxa de erosão diferente.

Este sistema é incapaz de simular um fluxo real de fluidos e rios, mas pode potencialmente ser adaptado a essas tarefas. Vou pensar sobre isso e, talvez, no futuro, lançarei uma continuação da postagem.

All Articles