Tenho formação em engenharia civil e realizo análises hidráulicas e hidrológicas regularmente. Eles vendem graus para esse tipo de coisa, mas na verdade não é ciência de foguetes. Recentemente, pensei em implementar todo o processo hidrológico e hidráulico de um terreno na GPU. Eu aprendi os shaders de computação apenas recentemente, então, atualmente, estou preso a ser melhor em engenharia do que projetando fluxos de trabalho de GPU paralelos.
Você pode calcular a quantidade de água gerada durante um evento de chuva usando a fórmula:
Q (CF/S) = c * I (in/hr) * A (acres)
Estou tendo dificuldades para ir além do cálculo da "área cultivada" mesmo da primeira área.
Visão geral da implementação atual:
- O terreno é uma grade regular de vértices em intervalos de 1 unidade
- Um mapa de altura contém um valor de altura R32 para cada vértice
- Atualmente, só estou permitindo o fluxo nas 4 direções cardeais (sem diagonais)
- Estou usando um Texture2D [int] como um estêncil para vértices que já analisei
Algoritmo atual:
- Quando a ferramenta de terreno estiver ativa e não estiver agora ...
- Limpe o "estêncil".
- Escaneie todo o terreno para a menor elevação.
- Esse ponto único é a entrada inicial para CS_Flood.
- CS_Flood faz um passe no eixo X.
- Cada vértice de entrada é projetado nas direções X e X + até 2048 vezes.
- Encontrar um vértice adjacente com uma coordenada OOB indica a aresta do terreno nessa direção. O CurrentPoint é anexado ao buffer BoundaryPoints e o loop de projeção para essa direção é finalizado. Isso foi fácil e funciona muito bem sempre.
- Vértices adjacentes com alturas> = a altura do vértice atual são marcados no estêncil e adicionados ao buffer NextPass.
- Vértices adjacentes com alturas <a altura atual do vértice indica o pico de uma crista e termina o loop de projeção. Uma iteração futura do preenchimento de inundação pode fluir em torno da base da cordilheira, subindo pela parte traseira e detectando a mesma cordilheira pela segunda vez.
- Quaisquer pontos de pico / cume detectados mais de uma vez não serão um BoundaryPoint, para essa finalidade.
- Quaisquer pontos de pico / cume detectados exatamente uma vez são anexados a BoundaryPoints e o loop de projeção nessa direção é encerrado.
- CS_Flood faz um eixo Z passar com o mesmo código, usando os pontos gerados pelo eixo X como entrada.
- No momento, o CS_Flood continua alternando entre as duas direções indefinidamente. Eventualmente, terminarei o loop geral sempre que o CS_Flood for concluído e o buffer NextPass estiver vazio.
Idealmente, nesse ponto, os BoundaryPoints conteriam cada vértice que ocorre na divisão de drenagem natural. As gotas de água que caem dentro dos limites acabam fluindo para o mesmo ponto baixo. As gotas de água que caem do lado de fora da fronteira vão para "outro lugar".
Então:
- Sem limpar o estêncil, digitalize novamente o terreno em busca do vértice mais baixo e sem estêncil.
- Iterar CS_Flood.
- Repita até que o estêncil esteja cheio (ou algo semelhante).
É difícil perceber o 3D com essas cores; isso mostra linhas de contorno em elevações integrais:
(um buraco cercado por uma berma perto da borda)
Existem cerca de 10 maneiras exclusivas de drenar um vértice; dando a cada uma uma cor única:
(marcas de ferramentas circulares visíveis, "sulcos" aparecem muito bem)
Isso mostra todos os pontos gerados pelo CS_Flood, limite ou não, como uma POINTLIST:
O algoritmo sempre quase funciona . Às vezes, até funciona corretamente. Outras vezes, o algoritmo está claramente contido na forma correta, mas continuará a produzir pontos indefinidamente. Como visto na terceira captura de tela, às vezes fica confuso. Deve haver outra situação / fator que eu negligenciei. Agradeço qualquer ajuda para encontrar minha supervisão ou sugestões de maneiras mais simples e / ou mais elegantes de atacar o problema.
MissingPoint! pode ser incluído auxiliando o algoritmo para adicionar todos os novos BoundaryPoint detectados ao buffer NextPass. Durante a próxima passagem, 99% dos pontos gerados por esse curativo desperdiçarão uma pequena quantidade de tempo na GPU, determinando que eles não podem ir a lugar algum e não fazer nada. Durante a primeira passagem, o envio do LowestPoint junto com os outros pontos NextPass também lidaria com esse cenário específico.
Sei que é plausível e, com tempo suficiente, poderei ajudá-lo o suficiente para fazer o que quero. Gostaria de fazê-lo de uma maneira melhor, mais inteligente e mais rápida, se possível, e ainda não tenho experiência suficiente para conhecê-lo melhor.
Respostas:
Quando uma gota "tentava" visitar um vértice, o estêncil era marcado com o
InterlockedExchange
uso do "valor original" para determinar se ele já estava estampado (mesmo que eu o tenha substituído).O melhor algoritmo que eu criei deu ao dilúvio um "bloco de rascunho" e uma única regra: "não desça ladeira abaixo" (alturas iguais ou superiores). Isso eliminou quase todos os testes complicados. Embora seja geralmente bom em não fluir sobre picos / cordilheiras, flui ao longo deles porque os vértices adjacentes são "planos". Ocasionalmente, isso permite que gotas passem furtivamente pelas cordilheiras.
Cada um dos pontos "longe demais" é "fluído" e "flui" para a área de drenagem (para em 1) ou não (para em 0). Os "nots" são descartados e o bloco de rascunho corrigido é copiado para o "final". Se a final já estiver estampada, o bloco de rascunho será descartado. (Futuro: essas colisões devem representar coletivamente o limite externo da atual área de drenagem.)
A 10FPS:
Os "nots" são mostrados em vermelho, uma vez que a área grande é copiada para a final e fica verde, o algoritmo se repete para as demais áreas não estilizadas.
fonte