- GodotOceanWaves ist ein Rendering-Experiment, das in der Godot Engine mithilfe der inversen Fourier-Transformation eines gerichteten Meereswellenspektrums offene Meeresoberflächen erzeugt und mit zur Laufzeit per Skript anpassbaren Parametern verschiedene Meeresumgebungen simuliert
- Gerstner waves eignen sich für niederfrequente Details ruhigen Wassers, stoßen bei rauen offenen Meeresoberflächen aber an Grenzen; dieses Projekt nutzt daher Spektren auf Basis empirischer ozeanografischer Daten und FFT
- Die Wellenerzeugung folgt dem Tessendorf-Ansatz: Es wird ein initialer Spektralzustand erzeugt, über eine Dispersionsrelation zeitlich fortgeschrieben und anschließend per inverser Fourier-Transformation werden Displacement-, Normal- und Foam-Maps erzeugt
- Das Rendering folgt dem BSDF aus dem Atlas-GDC-Vortrag, verwendet jedoch eine GGX-Verteilung; Foam wird in Bereichen akkumuliert und gedämpft, in denen die Jacobi-Matrix des Displacements negativ ist, und Sea Spray ist mit Godots GPUParticles3D und Custom Shadern umgesetzt
- Mehrere Wave Cascades, einstellbare Update-Intervalle und Lastverteilung pro Cascade adressieren Tiling und GPU-Arbeitslast; Cascades müssen jedoch dieselbe Spektrum-Texturauflösung verwenden, und der partikelbasierte Sea-Spray-Ansatz skaliert bei höherer Dichte ineffizient
Was GodotOceanWaves macht
- GodotOceanWaves ist ein Projekt zum Experimentieren mit dem Rendering offener Meeresflächen in der Godot Engine
- Für die Wellenerzeugung wird die inverse Fourier-Transformation eines gerichteten Meereswellenspektrums verwendet
- Die offengelegten Parameter sind kompakt gehalten und können per Skript in Echtzeit geändert werden, um verschiedene Meereswellen-Umgebungen zu simulieren
- Als Demo-Videos sind
ocean_demo.mp4undenvironment_demo.mp4enthalten
Warum Fourier-basierte Wellen statt Gerstner waves verwendet werden
- Eine gängige Methode zur Animation von Wasser in Spielen ist es, Vertices mit Gerstner waves zu verschieben
- Gerstner waves eignen sich gut, um niederfrequente Details ruhigen Wassers zu modellieren, reichen aber nicht aus, um die raue Oberfläche des offenen Meeres präzise darzustellen
- Mit Meereswellenspektren auf Basis empirischer Daten, die von Ozeanografen gesammelt wurden, lässt sich direkter steuern, welche Wellen und Parameter geändert werden müssen, um einen bestimmten Meereszustand zu erzeugen
- FFT hat mit
O(N log N)eine geringere Komplexität als die klassische diskrete Fourier-Transformation mitO(N^2)und lässt sich auf parallele Systeme erweitern, wodurch sie gut für die Ausführung auf der GPU geeignet ist - Bei Gerstner waves muss jeder Thread Berechnungen für
NWellen durchführen, während bei FFT-basierten Wellen jeder Thread nur Berechnungen in der Größenordnung vonlog(N)ausführt
Shading, Foam und Sea Spray
- Das Beleuchtungsmodell für das Meer folgt weitgehend dem BSDF aus dem Atlas-GDC-Vortrag
- Für die Mikrofacettenverteilung wird statt Beckmann GGX verwendet
- Der Grund ist, dass die flacheren und weicheren Highlights von GGX in mehreren getesteten Meereswellen-Umgebungen ein gleichmäßigeres Erscheinungsbild lieferten
- Normal-/Foam-Maps werden mit einer Mischung aus bicubic und bilinear Filtering gesampelt, abhängig von der Pixeldichte im World Space
- Bei niedriger Oberflächenauflösung werden Texture-Aliasing-Artefakte reduziert, bei hoher Oberflächenauflösung bleiben Details erhalten
- Sea Foam wird nach Tessendorfs Methode dort erzeugt, wo sich Wellenkämme nach innen einrollen, also dort, wo die Jacobi-Matrix des Displacements negativ ist
- Foam wird über mehrere Wellen-Updates hinweg linear in einer Textur akkumuliert
- Die Parameter
"foam grow rate"und"foam decay rate"steuern jeweils Akkumulation und exponentielle Dämpfung
- Sea Spray ist mit Godots
GPUParticles3D-Node und einem Custom Particle Shader umgesetzt- Partikel werden gleichmäßig auf einer Ebene innerhalb der Bounding Box von
GPUParticles3Dverteilt - Sie werden abhängig von der Foam-Menge an der jeweiligen Position gecullt, und nicht gecullte Partikel beginnen ihren Lebenszyklus mit einem zufälligen Offset
- Jedes Partikel verwendet ein Billboard Sprite mit einer einzelnen statischen Textur
- Während des Lebenszyklus werden Scale und Displacement verändert, um das Aussehen eines Splashs zu simulieren; ein Dissolve-Effekt im Mesh Shader vermittelt den Eindruck, dass Gischt in der Luft zerstäubt
- Partikel werden gleichmäßig auf einer Ebene innerhalb der Bounding Box von
- Der größte Nachteil dieses Sea-Spray-Ansatzes ist, dass selbst eine starke Erhöhung der Partikelzahl die Dichte nur geringfügig steigert
- Das liegt daran, dass die Partikel gleichmäßig in der Bounding Box verteilt sind und die meisten zusätzlich erzeugten Partikel gecullt werden
Pipeline der Wellensimulation
- Die Methode zur Erzeugung der Oberflächenwellen folgt eng Tessendorf
- Eine gerichtete Meereswellenspektrum-Funktion wird mit Zufallszahlen aus einer Gaussian distribution multipliziert, um den initialen Spektralzustand des Meeres zu erzeugen
- Der initiale Zustand wird über eine Dispersionsrelation, die Frequenz und Ausbreitungsgeschwindigkeit der Wellen verknüpft, über die Zeit fortgeschrieben
- Auf den fortgeschriebenen Zustand wird eine inverse Fourier-Transformation angewendet, um Displacement-Maps und Normal-Maps zu erzeugen
- Die Implementierung besteht aus einer Compute-Shader-Pipeline, die Godots
RenderingDevice-Abstraktion nutzt
Aufbau des Meereswellenspektrums
- Die gerichtete Meereswellenspektrum-Funktion
S(ω, θ)gibt die Energie einer Welle für eine gegebene Frequenzωund Richtungθzurück - Diese Funktion setzt sich aus der ungerichteten Spektrum-Funktion
S(ω)und der Directional-Spreading-FunktionD(ω, θ)zusammen; beide Entscheidungen sind voneinander unabhängig - Für das ungerichtete Spektrum wird das von Horvath beschriebene Texel-Marsen-Arsloe(TMA)-Spektrum verwendet
- TMA nutzt Windgeschwindigkeit
U, WassertiefeDund Fetch LengthF - TMA kombiniert das vorausgehende JONSWAP-Spektrum mit einer Funktion zur Dämpfung durch die Wassertiefe
- TMA nutzt Windgeschwindigkeit
- Für die Directional-Spreading-Funktion wird eine Mischung aus Horvaths flat und Hasselmann Directional Spreading über den
spread-Parameterμverwendet- Auch der von Horvath vorgeschlagene Parameter
swellξist enthalten, um die Elongation der Meereswellen zu modellieren - Das Hasselmann Directional Spreading wurde gewählt, weil es eine näherungsweise analytische Lösung für den Normalisierungsfaktor
Q(σ)bietet
- Auch der von Horvath vorgeschlagene Parameter
- Gemäß Tessendorfs Vorschlag wird die resultierende Spektrum-Funktion außerdem mit einem Term zur Unterdrückung kleiner Wellen multipliziert
- Dieser Term verwendet die Größe des Wellenzahlvektors
kund den Parameterdetailδ
- Dieser Term verwendet die Größe des Wellenzahlvektors
- Das finale gerichtete Meereswellenspektrum kombiniert TMA, mixed directional spread und small-wave suppression
GPU-FFT-Implementierung
- Für FFT wird eine Custom-GPU-Implementierung in Compute Shadern verwendet
- Statt Cooley-Tukey wird der Stockham-FFT-Algorithmus genutzt, um die initiale Bit-Reversal-Permutation zu vermeiden
- Nach Flügge wird eine
"butterfly"-Textur berechnet, die den Datenfluss der FFT kodiert- Diese Textur wird einmal neu berechnet, wenn sich die Spektrum-Texturauflösung ändert
- Die 2D-FFT wendet zunächst den FFT-Kernel zeilenweise auf die Spektrum-Textur an
- Anschließend wird die Textur per Compute Shader transponiert, sodass derselbe zeilenweise FFT-Kernel wie ein spaltenweiser FFT-Kernel wiederverwendet werden kann
- Die Transposition ermöglicht nicht nur die Wiederverwendung der Pipeline, sondern verbessert auch die Speicherzugriffsmuster
Wave Cascades und Umgang mit Tiling
- In der Ferne werden Tiling-Artefakte sehr deutlich, besonders wenn Sea Foam vorhanden ist
- Das Wellenerzeugungssystem adressiert dieses Problem, indem es mehrere Wave Cascades gleichzeitig layern kann
- Jede Cascade hat ihre eigene Tiling-Größe und ihren eigenen Parametersatz
- Cascades können zur Laufzeit dynamisch hinzugefügt und entfernt werden
- Da alle Cascades dieselbe Compute Pipeline verwenden, müssen sie dieselbe Spektrum-Texturauflösung haben
- Alternativ lassen sich Wellen-Displacement und Normals mit Noise blenden, um Tiling-Artefakte bei geringeren Performance-Kosten zu reduzieren
- Beim Überlagern von Cascades müssen Parameter und Größe jeder Cascade sorgfältig gewählt werden, um Welleninterferenzen zu vermeiden
- Auch die Wellenphase der Cascades muss offset werden, damit sie nicht miteinander interferieren
- Das System versucht dies automatisch, indem es die Startzeit jeder Cascade unterschiedlich offsettet
- Es gibt jedoch den Caveat, dass nicht sicher ist, ob dieser Ansatz tatsächlich funktioniert
Lastverteilung bei Updates
- Da Wellenbewegungen unregelmäßig sind, kann die Darstellung auch dann wahrnehmbar flüssig wirken, wenn Displacements nicht in jedem Frame aktualisiert werden
- Der Parameter
"update rate"steuert, wie oft pro Sekunde eine Wave Cascade aktualisiert wird - Das Überspringen von Frames reduziert die für FFT aufgewendete GPU-Zeit, auf Frames, in denen die Wellenerzeugungs-Pipeline läuft, kann es aber weiterhin zu Stutter kommen
- Experimente mit asynchroner Berechnung von Cascade-Updates über Godots lokales
RenderingDeviceverursachten durch Texturübertragungen zwischen CPU und GPU einen großen Performance-Overhead - Stattdessen versucht das System eine Lastverteilung der Cascades
- Wenn die Frame-Zeit kürzer als die update rate ist, wird pro Frame nur eine Cascade aktualisiert
- Dieser Ansatz reduziert Stutter und erhält zugleich die Vorteile der geringeren GPU-Arbeitslast durch Frame Skipping
Referenzmaterial
- Realtime GPGPU FFT Ocean Water Simulation: Fynn-Jorin Flügge, Hamburg University of Technology, 2017
- I Tried Simulating The Entire Ocean: Garrett Gunnell, 2023
- Empirical Directional Wave Spectra for Computer Graphics: Christopher J. Horvath, DigiPro, 2015
- Simulating Ocean Water: Jerry Tessendorf, SIGGRAPH, 2004
- Implementing Fast Fourier Transform Algorithms of Real-Valued Sequences: Robert Matusiak, Texas Instruments, 2001
- Wakes, Explosions and Lighting: Interactive Water Simulation in 'Atlas': Mark Mihelich, GDC, 2019
- FFT-Ocean: Ivan Pensionerov, GitHub, 2020
1 Kommentare
Hacker-News-Meinungen
Zwei weitere Godot-Repositories dieser Person sind ebenfalls sehr interessant. Mir gefällt, dass sie bei der Beschreibung ihrer Repositories viele Details liefert; besonders dieses sticht hervor: https://github.com/2Retr0/GodotGaussianSplatting
Ich frage mich, ob sie Student ist. Sie zitiert häufig andere Arbeiten und scheint kürzlich veröffentlichte Materialien ziemlich gut zu verstehen.
ENGR96A-coursework,Relevant coursework for ENGR 96A Introduction to Engineering Design F23, und den Commit-Daten nach scheint F23 für das Herbstsemester 2023 zu stehen.Natürlich könnten die im Repository erwähnten UCLA-Kurse auch öffentlich zugänglich sein; man muss also vielleicht nicht im klassischen Sinn an der UCLA eingeschrieben sein, um sie zu belegen.
Vor 20 Jahren hätte man wahrscheinlich monatelang an der Meeresoberfläche im Rendering gefeilt und wäre trotzdem nicht annähernd auf dieses Niveau gekommen. Wirklich ein sehr gut gemachtes Ergebnis.
Allerdings wirkt das Demo-Video etwas überzeichnet. Aus Sicht von jemandem mit über 50.000 Seemeilen Hochseefahrt im Logbuch müssten bei so scharfen, hohen Wellen deutlich stärkere Winde wehen. Das dürfte sich aber wohl einfach über Parameter anpassen lassen.
Wenn das in Godot gemacht ist, kann man dann von Echtzeit-Rendering ausgehen? Ich frage mich auch, ob dafür eine Monster-GPU nötig ist.
Außerdem wirkt die Gischt hier wie ein nachträglicher Effekt; in der echten See entschärft Gischt die Schärfe der Wellen, was sich auch visuell subtil zeigt.
Natürlich steckt in diesem eleganten, einfachen und effektiven Modell kaum Physik. Daher bitte eher als Herumstochern und Vorschläge für mögliche Richtungen verstehen, nicht als Kritik mit verschränkten Armen.
Eines meiner Lieblingsvideos zu diesem Thema: https://m.youtube.com/watch?v=yPfagLeUa7k
Dieses ist auch gut: https://m.youtube.com/watch?v=kGEqaX4Y4bQ
Stattdessen verbringe ich jedes Wochenende damit, am Wasser-Shader herumzuspielen, bis er so aussieht, wie ich es will. Mein Spiel ist allerdings ein Puzzle-Spiel und braucht gar kein Wasser. Jetzt habe ich lediglich einen schicken Startbildschirm.
Heute ist das nur noch eine Checkbox in einer Game-Engine, und davon gibt es unzählige. Viele können sich gar nicht vorstellen, wie gut die Umgebung dank moderner Engines geworden ist.
Ich will die Leistung dieser Arbeit nicht schmälern; sie ist wirklich hervorragend, und dem Entwickler gebührt Lob. Ich wollte nur sagen, in was für guten Zeiten wir inzwischen leben.
Trotzdem sieht es sehr gut aus, und nach meinem jüngsten oberflächlichen Suchen gehört es vermutlich zum Besten, was derzeit möglich ist.
Persönlich würde ich bei Hochseewellen gern mehr Rhythmus sehen. Ich frage mich, ob eine Simulation von 15 Knoten Wind, der mehrere Stunden lang über 1 km Wasseroberfläche weht, dem entspricht, was man tatsächlich beobachtet: relativ geordnete Brecher, die entstehen und sich wieder zerstreuen.
Solche Dinge haben mich einst in die Computerwelt gezogen, aber irgendwann bin ich in ein einfaches, langweiliges Leben geraten, in dem man Bibliotheken, Endpoints und unternehmerischen Bullshit zusammenklebt, was in Burnout mündet.
Ich hoffe, irgendwann wieder dorthin zurückzufinden.
Probier es aus. Wir drücken dir alle die Daumen.
Der Ansatz ist anders, aber wer sich für Wellen-/Ozeansimulation interessiert: Acerola hat dazu wie immer ein hervorragendes Video gemacht. https://youtu.be/PH9q0HNBjT4 und https://youtu.be/yPfagLeUa7k
Edit: Eines davon war in den Referenzen bereits verlinkt, aber unter dem echten Namen statt dem Nickname, daher habe ich es erst jetzt bemerkt.
Interessant, wie schwierig dieses Problem ist. Seit Jahrzehnten versucht man es, und trotzdem steckt es immer noch im Uncanny Valley.
Wenn man diese Szene anhält, sehen die aufragenden Wellen wie schneebedeckte Berge aus. Damit Wasser so scharfe Formen annimmt, müsste der Wind ziemlich stark sein, aber Wind bläst nun einmal nicht direkt nach oben, daher wirkt es unrealistisch. Solche scharfen Merkmale müssten eine Richtung haben.
Die Eigenschaften von Dünung sind aber ziemlich gut getroffen. Die Auf-und-ab-Bewegung ist größer als die horizontale Bewegung, was man auf offener See erwarten würde.
Ich weiß nicht, wie sie Lichtdurchlässigkeit simulieren, aber es funktioniert gut. Das Licht, das bei Sonnenauf- oder -untergang zwischen den Wellen hindurchscheint, ist großartig und macht den Effekt sehr glaubwürdig.
Man könnte es etwas malerisch nennen, aber es sieht nicht falsch aus.
Ich habe mehrere Jahre am Strand gelebt. Wenn man am Strand lebt, ist das Meer faszinierend und sinnlich ansprechend, sodass man stundenlang darauf schauen kann. Bei diesem hier hätte ich keinen Moment gedacht, dass es gerendert ist.
Auf Shadertoy gibt es ebenfalls ein gutes Beispiel: https://www.shadertoy.com/view/Ms2SD1
Da Fourier auf die Idee der Frequenztransformation von Wellen kam, während er Gezeitenwellen maß, schließt sich in gewisser Weise fast der Kreis.
Sehr beeindruckend.
Großartig, aber dieser Ansatz wird bei wirklich großen Wellen wegen der Nichtlinearität bis zu einem gewissen Grad zusammenbrechen. Wasserwellen sind keine Sinuswellen, aber bei kleinen und mittelhohen Wellen sind sie ähnlich genug, sodass diese Methode sehr gut funktioniert.
Sehr große Wellen weichen nicht nur viel stärker von Sinuswellen ab, sondern beginnen auch, nichtlinear miteinander zu interagieren; das kann die hier verwendete lineare Überlagerung nicht erfassen.
Für die meisten Seegangsbedingungen ist es daher fantastisch. Wenn man aber Wellen wie in „Der Sturm“ realistisch erzeugen will, braucht man einen anderen Ansatz.
Sehr beeindruckend und besser als alles, was ich bisher gesehen habe, aber die Dünung wirkt etwas daneben. Wenn ich es erklären müsste: Hochfrequente Wellen bewegen sich wohl nicht so über niederfrequente Wellen hinweg, wie es im Video aussieht.