J'ai une formation en génie civil et je réalise régulièrement des analyses hydrauliques et hydrologiques. Ils vendent des diplômes pour ce genre de choses, mais ce n'est vraiment pas sorcier. J'ai récemment pensé à implémenter l'ensemble du processus hydrologique et hydraulique d'un terrain sur le GPU. Je n'ai appris les shaders de calcul que récemment, donc je suis actuellement coincé à être meilleur en ingénierie que de concevoir des workflows GPU parallèles.
Vous pouvez calculer la quantité d'eau générée lors d'un événement pluvieux à l'aide de la formule:
Q (CF/S) = c * I (in/hr) * A (acres)
J'ai du mal à aller au-delà du calcul de la "superficie" même de la première zone.
Aperçu de l'implémentation actuelle:
- Le terrain est une grille régulière de sommets à intervalles d'une unité
- Une carte de hauteur contient une valeur de hauteur R32 pour chaque sommet
- Actuellement, je n'autorise le flux que dans les 4 directions cardinales (pas de diagonales)
- J'utilise un Texture2D [int] comme gabarit pour les sommets que j'ai déjà analysés
Algorithme actuel:
- Lorsque l'outil de terrain a été actif et n'est pas, maintenant ...
- Effacez le "pochoir".
- Scannez le terrain entier pour trouver l'altitude la plus basse.
- Ce point unique est l'entrée initiale de CS_Flood.
- CS_Flood effectue une passe sur l'axe X.
- Chaque sommet d'entrée est projeté dans les directions X- et X + jusqu'à 2048 fois.
- La recherche d'un sommet adjacent avec une coordonnée OOB indique le bord du terrain dans cette direction. CurrentPoint est ajouté au tampon BoundaryPoints et la boucle de projection pour cette direction est terminée. C'était facile et fonctionne très bien à chaque fois.
- Les sommets adjacents avec des hauteurs> = la hauteur du sommet actuel sont marqués dans le gabarit et ajoutés au tampon NextPass.
- Les sommets adjacents avec des hauteurs <la hauteur du sommet actuel indiquent le pic d'une crête et terminent la boucle de projection. Une itération future du remblai inondable pourrait s'écouler autour de la base de la crête, sur le côté "arrière" de celle-ci et détecter la même crête une deuxième fois.
- À cette fin, aucun point de crête / arête détecté plus d'une fois ne sera un BoundaryPoint.
- Tous les points de crête / crête détectés exactement une fois sont ajoutés aux BoundaryPoints et la boucle de projection dans cette direction est terminée.
- CS_Flood effectue une passe d'axe Z avec le même code, en utilisant les points générés par la passe d'axe X en entrée.
- À l'heure actuelle, CS_Flood continue d'alterner indéfiniment entre les deux directions. Finalement, je terminerai la boucle globale chaque fois que CS_Flood se termine et que le tampon NextPass est vide.
Idéalement, à ce stade, les points limites devraient contenir chaque sommet qui se produit sur la fracture de drainage naturelle. Les gouttes d'eau qui atterrissent dans la limite finissent par s'écouler vers le même point bas. Des gouttes d'eau atterrissant en dehors de la frontière vont "ailleurs".
Alors:
- Sans effacer le pochoir, scannez à nouveau le terrain pour le sommet le plus bas, non pochu.
- Itérer CS_Flood.
- Répétez jusqu'à ce que le pochoir soit plein (ou quelque chose de similaire).
La 3D est difficile à percevoir avec ces couleurs; cela montre des courbes de niveau à des altitudes intégrales:
(un trou entouré d'une berme près du bord)
Il existe environ 10 façons uniques de drainer à travers un sommet; donner à chacun une couleur unique ressemble à:
(les marques d'outils circulaires visibles, les "crêtes" apparaissent bien)
Cela montre chaque point généré par CS_Flood, frontière ou autre, en tant que POINTLIST:
L'algorithme fonctionne toujours presque . Parfois, cela fonctionne même correctement. D'autres fois, l'algorithme est clairement contenu à la forme correcte, mais continuera à produire des points indéfiniment. Comme on le voit dans la 3ème capture d'écran, parfois cela devient confus. Il doit y avoir une autre situation / facteur que j'ai négligé. J'apprécierais toute aide pour trouver ma surveillance ou des suggestions de façons plus simples et / ou plus élégantes d'attaquer le problème.
MissingPoint! peut être inclus en aidant l’algorithme à ajouter une bande à chaque nouveau BoundaryPoint détecté dans le tampon NextPass. Lors de la prochaine passe, 99% des points générés par ce pansement perdront un peu de temps GPU à déterminer qu'ils ne peuvent aller nulle part et ne rien faire. Lors de la première passe, l'envoi de LowestPoint avec les autres points NextPass gérerait également ce scénario spécifique.
Je sais que c'est plausible et, avec suffisamment de temps, je pourrai le panser suffisamment pour faire ce que je veux. J'aimerais le faire d'une manière meilleure, plus intelligente et plus rapide, si possible, et je n'ai pas encore assez d'expérience pour mieux savoir.
Réponses:
Lorsqu'une goutte "essayait" de visiter un sommet, le pochoir était marqué en
InterlockedExchange
utilisant la "valeur d'origine" pour déterminer s'il était déjà au pochoir (même si je viens de l'écraser).Le meilleur algorithme que j'ai trouvé a donné au déluge un "bloc-notes" et une seule règle: "ne pas couler en descente" (hauteurs égales ou supérieures). Cela a éliminé presque tous les tests compliqués. Bien qu'il soit généralement bon de ne pas s'écouler sur des pics / crêtes, il coule le long de ceux-ci car les sommets adjacents sont "plats". Cela permet parfois aux gouttes de se faufiler au-delà des lignes de crête.
Chacun des points «trop loin» est alors «coulé» et coulera «dans» la zone de drainage (s'arrête à 1) ou non (s'arrête à 0). Les "nots" sont rejetés et le bloc-notes corrigé est copié dans le "final". Si la finale est déjà au pochoir, le bloc-notes est jeté. (Futur: ces collisions devraient représenter collectivement la limite extérieure de la zone de drainage actuelle.)
À 10FPS:
Les "nots" sont affichés en rouge, une fois que la grande zone est copiée dans la finale et devient verte, puis l'algorithme se répète pour les zones non marquées restantes.
la source