Arazi Üzerinde Su Simülasyonu
(lisyarus.github.io)- Arazinin kazılıp doldurulabildiği oyunlarda göllerin, nehirlerin ve su birikintilerinin yeni sınırlardan akıp gitmesi gerektiğinden hızlı ve kararlı ızgara tabanlı su simülasyonu gerekir
- Hedef, yaklaşık 1 m ölçeğinde araziyle aynı ızgarayı kullanan; suyun korunması, kontrol edilebilir kararlılık ve doğrusal güncelleme maliyeti sağlayan 2B bir yükseklik alanı modelidir
- Smoothed Particle Hydrodynamics ve Stable Fluids, sırasıyla yüksek çözünürlüklü parçacık akışkanlarına ve kapalı akışkan hacimlerine uygun olduğundan, arazi üzerindeki serbest yüzeyi hızlı işleme koşuluyla örtüşmez
- Benimsenen virtual pipes yöntemi, staggered grid üzerinde su yüksekliğini ve hücreler arası akışı saklar; akış ivmelendirme, dışa akış ölçekleme ve su sütunu güncellemesini birkaç 2B dizi döngüsüyle işler
- Uygun
dtvegdeğerlerinde su gibi görünen sonuçlar üretir, ancak eylemsizlik ve hız yayılımı olmadığından hızlı su akıntılarının bir gölün içine doğru ilerleyerek yayılmaması gibi bir sınır kalır
Arazi değiştirilebilen oyunlarda su neden zordur
- Strateji oyunlarında ya da şehir/köy simülasyonlarında su; doğal sınırlar, denizcilik, balıkçılık, ticaret, deniz savaşları, içme suyu, taşımacılık ve görsellik gibi roller üstlenebilir
- Arazinin doğrudan değiştirilebilmesi su işlemeyi çok daha zor hale getirir
- Toprak, kum, kil gibi kaynaklar yerden çıkarıldığında arazinin de birlikte kaldırılması daha doğal görünür
- Taş ve metal cevherleri de yüzeydeki nesneler olarak durmaktansa, toprağı kazarak çıkarılan kaynaklar olmaya daha uygundur
- Eğimli yüzeylere bina yapılamıyorsa inşaattan önce arazinin düzleştirilmesi gerekir
- Yaratıcı ifade aracı olarak arazi düzenleme başlı başına sunulabilir
- Temel durum, bir gölün ya da su birikintisinin sınırı kazılıp bir su yolu açıldığında suyun nereye ve ne kadar akacağıdır
Basit çözümlerin yetersiz kaldığı noktalar
- Olası geçici çözümler arasında suyu başlangıç konumunda sabitlemek, belirli bir yüksekliğin altındaki her şeyi su kabul etmek, çok derin kazmayı engellemek veya Minecraft/Dwarf Fortress tarzı basit akış modelleri kullanmak yer alır
- Bu yöntemler alternatif olarak mümkün olsa da ana model olarak kullanılmak için fazla basit ya da fazla bloklu hisseder
- Dwarf Fortress modeli diğer örneklere göre daha yakındır, ancak 3B varsayımıyla tasarlanmıştır; gereken problem ise ağırlıklı olarak 2B arazi üzerindeki sudur
- Timberborn burada ele alınanla aynı modeli kullanır
İstenen simülasyon koşulları
- Hedeflenen su modeli şu koşulları sağlamalıdır
- Araziyle aynı ızgara üzerinde çalışması tercih edilir
- Ortalama ölçek yaklaşık 1 m civarındadır; küçük su sıçramalarını simüle etmek gerekmez
- Su, arazi üzerinde bir yükseklik alanı olarak görülür; dikey akışlar veya dikey kesitteki boşluklar dikkate alınmaz
- Su akmalıdır ve simülasyon hataları nedeniyle yok olmamalıdır
- Kararlılığı kontrol edilebilir olmalıdır
- Bir adımın maliyeti simülasyon boyutuna göre doğrusal olmalı; ideal olarak birkaç döngüyle biten bir yapı olmalıdır
Mevcut akışkan simülasyonlarıyla uyumsuzluk
- Smoothed Particle Hydrodynamics etkileyici yüksek çözünürlüklü akışkan sonuçları üretir, ancak burada gereken problemden farklıdır
- 1 m boyutundaki su parçacıkları su balonu gibi görünebilir
- Parçacıklar küçültülürse performans maliyeti artar
- Hedef yüksek çözünürlüklü gerçekçilik değil, hızlı ve makul görünen bir modeldir
- Jos Stam’in Stable Fluids yaklaşımı, kapalı bir tank gibi akışkanla dolu hacimleri ele alan modele daha yakındır
- Arazi üzerindeki serbest yüzeyi doğrudan ele alma probleminden farklıdır
- Bazı adımlarda seyrek doğrusal sistemleri yinelemeli olarak çözmek gerektiğinden maliyetlidir
- Tüm Navier-Stokes denklemlerini çözmeye yönelik bir yaklaşımdır; burada gereken ise shallow water equations’tır
Shallow water equations ve ızgara seçimi
- Shallow water equations, arazi üzerindeki su katmanını dikey yönde ortalayıp 2B denklemlerle ele alan bir yaklaşımdır
- “Shallow”, su sütununun dikey boyutunun ilgilenilen yatay ölçekten çok daha küçük olduğu varsayımını ifade eder
- Nehir derinliğinin birkaç metreden onlarca metreye, ilgi mesafesinin ise kilometre ölçeğine çıktığı durumlarda geçerli olabilir
- Yaygın collocated grid, su yüksekliğini ve hızı aynı hücrede saklar; ancak akışkan dinamiğinde sorun çıkarabilir
- Birinci türev safça ayrıklaştırılırsa yön yanlılığı veya kararsızlık oluşabilir
- Soldan ve sağdan giren, yukarıdan ve aşağıdan çıkan akışların aynı hücrede bulunması, toplam hızın 0 gibi görünmesi çelişkisini yaratır
- Staggered grid, su yüksekliği ve yoğunluk gibi değerleri hücrelerde, hız veya akış değerlerini ise hücreler arasındaki kenarlarda saklar
N x Nsu yüksekliği dizisi(N+1) x NX yönü akış dizisiN x (N+1)Y yönü akış dizisi
Virtual pipes yöntemi
- Virtual pipes, su hücrelerinin sanal borularla birbirine bağlı olduğunu varsayıp akışı hesaplayan bir yöntemdir
- Başvurulan makalelerden biri çok seviyeli su sütunlarını ve dikey bağlantıları da ele alır; diğeri ağırlıklı olarak hidrolik erozyonu konu alır, ancak burada bu hedef dışarıda bırakılmıştır
- Saklanan değer sayısı 3’tür
water: her hücrenin su sütunu yüksekliğiflowX: yatay komşu hücreler arasındaki toplam su akışıflowY: dikey komşu hücreler arasındaki toplam su akışı
- Hız yerine akış (flow, flux) saklanır
- Akış, birim zamanda geçen su hacmi olarak görülebilir
- Boş hücreler arasındaki akış doğal olarak 0 diye tanımlanır
- Hız, akışın kesit alanına bölünmüş halidir; su neredeyse yokken 0/0 veya eşik değeri sorunları doğurabilir
Bir adımı oluşturan 3 aşama
- Bir simülasyon adımı üç aşamaya ayrılır
- Akış ivmelendirme: Komşu hücrelerin su yüzeyi yükseklik farkına göre hücreler arası akışı artırır
- Dışa akış ölçekleme: Bir hücreden çıkan su, hücrenin gerçekten sahip olduğundan fazlaysa dışa akışları azaltır
- Su sütunu güncellemesi: Komşu akışlara göre her hücrenin su yüksekliğini artırır veya azaltır
-
Akış ivmelendirme
- Komşu iki hücrenin su yükseklikleri farklıysa akış yüksek taraftan alçak tarafa doğru ivmelenir
- X ve Y yönlerindeki iç kenarlar için akış güncellenir;
g,dt,dx,dykullanılır - Sanal boru kesit alanı
Ayalnızcagile çarpım olarak kullanıldığından basit kullanımlardagiçine katılmış gibi ele alınabilir - Sürtünme, her adımda akışı azaltacak şekilde eklenir
- Makale
pow(friction, dt)katsayısını önerir - Daha sezgisel değerlerle kullanmak için
pow(1-friction, dt)tercih edilebilir friction=0, önceki akışı tamamen yok eden maksimum sürtünme;friction=1ise sürtünme yok şeklinde görülebilirdtbüyüdükçe simülasyon hızlanır ancak kararsızlaşabilir- Akışkan simülasyonlarında Courant-Friedrichs-Lewy condition önemlidir
- Pratikte
dtkararlı olana kadar küçültülmelidir; kullanılan değerler kabaca0.001ile0.01arasındadır
-
Su sütunu güncellemesi
- Her hücre komşu dört akışa bakarak su ekler veya çıkarır
- Soldan ve alttan gelen
flowX(x,y),flowY(x,y)eklenir - Sağa ve yukarı çıkan
flowX(x+1,y),flowY(x,y+1)çıkarılır - Hesaplanan akışlara göre suyun hücreler arasında gerçekten taşındığı aşamadır
-
Dışa akış ölçekleme
- Akış çok büyükse güncellemeden sonra bazı hücrelerin su yüksekliği negatif olabilir
- Her hücre için yalnızca çıkan akışlar toplanır ve bir adımda kaldırılacak suyun gerçek su miktarını aşıp aşmadığı kontrol edilir
- Kaldırılacak miktar çok büyükse çıkan akışlar aynı oranda azaltılarak su yüksekliğinin 0 veya üzerinde kalması sağlanır
- Bu aşama, negatif su miktarını engelleyen temel kararlılık mekanizmasıdır
Arazi, sınır koşulları ve viskozite işleme
- Arazi, akış ivmelendirme aşamasında su sütunu yüksekliği yerine su yüzeyi yüksekliği kullanılarak yansıtılır
- Su yüzeyi yüksekliği =
terrain(x,y) + water(x,y) - Arazisi yüksek olan hücre, aynı su sütunu yüksekliğine sahip olsa bile yüzeyi daha yüksek olduğundan suyu hareket ettirebilir
- Su yüzeyi yüksekliği =
- Sınır koşulları sınır akış değerleriyle örtük olarak belirlenir
flowX(0,y),flowX(N,y),flowY(x,0),flowY(x,N)sınıra karşılık gelir- 0 bırakılırsa duvar gibi çalışır
- Giriş değerleri su ekler, çıkış değerleri suyu kaldırır
- Arazi üzerindeki suda, harita kenarındaki suyun kaybolduğu dışa akış sınırı doğal olabilir
- Bir nehrin sınırı geçtiği kısımlar giriş sınırı yapılarak nehir suyunun akması sağlanabilir
- Sınır akışları her simülasyon adımının başında yeniden ayarlanmalıdır
- Çünkü dışa akış ölçeklemesi sınır akışlarını değiştirerek dışa akış sınırını duvar gibi davranır hale getirebilir
- Makalede su yüksekliğine göre akışı azaltan bir viskozite terimi de vardır
- Fikir, ince su katmanlarının iç kuvvetler nedeniyle zor hareket ettiği; kalın su katmanlarının ise daha özgürce hareket ettiğidir
- Magma akışı gibi yerlerde yararlı olabilir
- Suda kullanılmamıştır ve büyük arazi ölçeğinde viskozite etkisi neredeyse yoktur
Uygulama akışı ve performans biçimi
- Kodun tamamı şu sırayla yapılandırılmıştır
- Sınır akışlarını başlatma
pow(1-friction, dt)sürtünme katsayısını önceden hesaplama- X akışını ivmelendirme
- Y akışını ivmelendirme
- Negatif su miktarını önlemek için dışa akış ölçekleme
- Su sütununu güncelleme
- Simülasyonun büyük kısmı birkaç 2B diziyi dolaşan 4 döngü ve basit formüllerle tamamlanır
- C++ güncelleme kodunun tamamı water_2d.cpp içinde görülebilir
- Video örneği birkaç gün önce yayımlanan WebGPU water simulator projesinden alınmıştır; videodaki parçacıklar görselleştirme içindir ve simülasyona katılmaz
- Uygun
dtvegdeğerleri bulunduğunda kararlı görünür, gereksinimleri karşılar ve su gibi görünen sonuçlar üretir
Kalan sınırlamalar
- Bu modelde eylemsizlik ve hız yayılımı yoktur
- Hızlı bir su akıntısı göle girse bile gölün içine doğru yayılmayı sürdürmez, her yöne dağılır
- Su yüksekliği aynıysa ters yönlerdeki paralel iki su akıntısı birbirleriyle etkileşmeden var olabilir
- Su bir bölgeye ilk girdiğinde dalgalar oluşur ve bu biraz tuhaf görünebilir
Altıgen ve üçgen ızgaralara genişletme
- Hedef oyun kare ızgara değil, düzenli bir üçgen ızgara kullanır
- Üçgen ızgara, altıgen ızgaranın dual’i olarak görülebilir
- Komşu altıgen merkezleri çizgilerle birleştirildiğinde düzenli bir üçgen ızgara oluşur
- Red Blob Games’in hexagonal grids yazısında axial coordinate system’ın pointy-top hex yönünün dual ızgarasında kullanılmasına benzer
- Üçgen ızgara da hafif eğik genel bir 2B dizide saklanabilir
- Su sütunu yüksekliği, su yüzeyi render’ını kolaylaştırmak için ızgara köşelerinde saklanır
- Akış üç yöne ayrılır
- X yönü akışı
- Y yönü akışı
- Z yönü akışı
N x Nköşe ızgarasında şu diziler kullanılır(N+1) x NX akış dizisiN x (N+1)Y akış dizisi(N+1) x (N+1)Z akış dizisi; ancak bottom-left ve top-right değerleri kullanılmaz
- Kare ızgarayla karşılaştırıldığında sınır koşullarına, ivmelendirmeye, dışa akış ölçeklemeye ve su güncellemesine Z akışını eklemek yeterlidir
- En zor kısım indekslemeyi yanlış yapmamaktır
- Üçgen/altıgen ızgaralar için C++ kodu water_2d_hex.cpp içinde görülebilir
- Bu yöntem kare ızgaraya göre biraz daha izotropik olabilir
1 yorum
Hacker News yorumları
Akışkan simülasyonuna dair farklı bir yaklaşım olarak Coding Adventure videoları var
Rendering Fluids: https://www.youtube.com/watch?v=kOkfC5fLfgE
I Tried Putting my Fluid Simulation on a Planet: https://www.youtube.com/watch?v=8nIB7e_eds4&t=817s
GitHub: https://github.com/SebLague/Fluid-Sim?tab=readme-ov-file
[0] https://www.youtube.com/playlist?list=PLFt_AvWsXl0dT82XMtKAT...
[1] https://github.com/SebLague/Geographical-Adventures
Prosedürel üretimli oyunlarda hidrolojik simülasyonun zor olmasının nedenlerinden biri, su biriktiğinde çevredeki hücreleri etkilemesi ve bu etkinin de diğer komşu hücrelere yayılmaya devam etmesi
Prosedürel üretim çoğu zaman paralelleştirmeye uygundur, ama tam da paralelleştirmenin en çok gerekli göründüğü sonsuz alanlarda bu hesaplamaları düzgün biçimde paralelleştirmek zordur
Bu konunun çok fazla araştırıldığını görmedim; bu alanda çalışanlar arasında https://nickmcd.me sitesini özellikle beğeniyorum. Şimdiye kadar gördüğüm en iyi prosedürel arazilerden bazıları orada
Ancak o çalışmada da simülasyonun tasarımı nedeniyle alan sınırlı. Olası bir çözüm olarak, kırılamayan havza sınırlarını prosedürel olarak üretip sonra tüm havzayı tek seferde paralel simüle etmek en iyi yaklaşım gibi görünüyor
Çok ilginç bir problem ama bilgi alanımın dışında olduğu için daha çok gözlemci konumundayım
Bunlar, belirli bir noktadaki değeri etkileyebilecek alan ile o noktanın değerinin etkileyebileceği alanı ifade eder; yani yukarıda söylenenlerle doğrudan bağlantılıdır. Bazı durumlarda bu alanlar önceden bilinebilir
Örneğin 10 saatlik adımı simüle etmek için 10 ızgara hücresi kalınlığında bir sınır koyarsınız. Her bölgede 10 adımı hesapladıktan sonra, paralel hesaplanan diğer sınır simülasyonlarıyla sınır durumunu senkronize eder ve bunu tekrarlarsınız
Biraz konu dışı ama, yazıda kaynak toplamak için araziyi değiştirmenin gerekli olduğundan bahsedilen kısım aklıma takıldı
Animal Crossing'in bunu arazi deformasyonu olmadan da oldukça akıllıca ve verimli şekilde çözdüğünü hep düşünmüşümdür. Ağaç kestiğinizde kütük çıkıyor ama yalnızca belli miktarda; yani fiilen bir bekleme süresi oluşuyor
Pahalı arazi deformasyonu olmadan da geri bildirim ve sınırlı kaynak hissi verebilirsiniz. Elbette her oyuna uymaz ve küçük haritalarda daha iyi çalışır, ama düşünmeye değer. Oyunun gerçekten ihtiyacı yoksa arazi deformasyonu yapmamak çoğu zaman daha iyidir
Kaynak dağıtma yöntemi olarak standart bir yaklaşım, ama bekleme süresi sonsuz kaynak sorununu çözmüyor; yalnızca yavaşlatıyor. Ayrıca biraz sıkıcı ve etkisi zayıf
Bu konuyu tertemiz ele alan bir yazı ve Timberborn'a değinmesine sevindim
Bu aralar oyuna tamamen sardım; hâlâ denemediyseniz kesinlikle tavsiye ederim. Fizik tabanlı su akışı oyundaki başka bir karakter gibi ve suyu tutup motorlarda kullanmanın ve tarlalara ulaştırmanın yolunu bulmak temel oyun döngüsünü oluşturuyor
Eğlenceli ve uygulaması da gerçekten çok iyi olmuş. Böyle şeyler geliştirirken en büyük risk, güzel sonuçlara bakıp parametrelerle oynayarak saatleri harcamak
2011'de bir makale çalışması için bizzat GPU tabanlı akışkanlar dinamiği uyguladığımı hatırladım. Yüzey, yani doku üzerinde akan bir akışkan olarak kanı ele almıştım; 2D'de simüle edip ardından yerçekimi ve yüzey eğimini dikkate alarak mesh üzerine projekte etmiştim
Kısa bir videoyu da YouTube'a yüklemiştim: https://youtu.be/4vGrNc-GGW8
Gerçekten harika
Yakın zamanda o3-mini-high yardımıyla benzer bir fikri denedim. Algoritma fikrini anlattım; o da manuel müdahale olmadan bunu 3D olarak uygulayıp render etti. Yine de prompt'u birkaç kez vermem gerekti
https://3d-water-sim.netlify.app/
Henüz kusursuz değil ama bunun sebebi kurcalamayı bırakmış olmam; her yinelemede oldukça belirgin biçimde iyileşiyordu. İlginç olan, arazi üretimi için bir yerlerden CDN benzeri bir şey çekmek yerine, Perlin gürültüsünün çalışan bir sürümünü sıfırdan düzgün biçimde uygulamış olması
Bu, yolculuk ile varış noktası arasındaki farka dair bir soru
Yazıdaki “Bu modelde atalet ve hız yayılımı yok. Hızlı bir su akışı göle girse bile gölün içine doğru daha fazla yayılmaz ve birikmiş ataleti yok sayarak her yöne dağılır. Su seviyesi aynıysa, ters yönlerde akan iki paralel su akışı da birbiriyle etkileşmeyebilir” denen kısım, aynı yöndeki çevredeki 6 akış oku ile ortalama alınarak çözülebilir gibi görünüyor
Bunun için öndeki ve arkadaki oklara büyük ağırlık, yanlardaki oklara ise küçük ağırlık verilir. Örneğin böyle oklar olduğunda
-a-> -b->
-c-> -d-> -e->
-f-> -g->
New_d = d * (1 - 2*.1 - 4*.01) + (c+e).1 + (a+b+f+b).01
Buradaki .1 ve .01 rastgele seçilmiş ağırlıklar olduğu için ayarlanması gerekir; titreşimi azaltmak için kullanıldığı gibi üsler de eklenebilir. O katsayı da dahil edilirse şöyle olabilir
New_d = d * (1 - 2*.1 - 4*.01 - .001) + (c+e).1 + (a+b+f+b).01
grid 0: her hücrenin su yüksekliği
grid 1: her kenardaki su akışı, yani 1. türev
grid 2: her hücrenin su ivmesi, yani 2. türev
Her ızgara bir önceki ızgaranın düal ızgarası ve onun türev değerini depolayan bir yapı. Aslında kenar verisini özel ele almaya gerek kalmadan yalnızca köşe verisi tutup tamamen düal ızgara olarak işlemek de mümkün gibi görünüyor. Kenar akışı, o kenarın iki ucundaki köşe akışlarının toplamından türetilebilir
Buna göre akışla akışkan yüksekliği güncellenir, hangi hızla ne kadar akışkan kütlesinin hücreye girdiğine göre ivme güncellenir, ardından ivme ve mevcut akışkan yüksekliğiyle akış güncellenir. Akışkanlar dinamiğini çok iyi bilmiyorum ama sayısal simülasyon açısından doğru görünüyor ve çapraz akış da mümkün hale geliyor
Yazıda da dendiği gibi hesaplama maliyeti çok daha fazla olacağı için, bu kadar gerçekçiliğin pratik kullanım senaryosu için gerçekten gerekli olup olmadığına önce bakmak gerekir
Bunu birkaç yıl önce meraktan yaptığım kaba bir çalışma: https://aperocky.com/hydrosim/
Bu kişisel proje soğuk depolama rafına kaldırılmadan önce erozyonu nasıl ele alacağımı çözememiştim. Yazarın bu kısma değinip denklemleri de eklemesini beğendim
Yakın zamanda benzer bir şeyi yayımladım. İçinde rastgele heightfield üretimi, tortu taşınımı ve erozyon da var: https://github.com/Ono-Sendai/terraingen
Şirketimizdeki harika bir geliştiricinin araştırma projesinin parçası olarak yaptığı eğitsel bir sel simülasyonunu doğrudan deneyebilirsiniz
https://flood.concord.org/
Belirgin bir etki görmek için alttaki araç çubuğunda model değerlerini değiştirmeniz gerekir
Bu, WebGL'de komşu hücreleri temel alarak hücre değerlerini hesaplayan hücre tabanlı bir simülasyon. Bu hesabı yapan shader burada
https://github.com/concord-consortium/flooding-model/blob/ma...