Ich versuche, ein voxelbasiertes Terrain-Generierungssystem in Unity3d (C #) zu implementieren. Ich habe erfolgreich ein einheitliches 3D-Rastersystem implementiert und die Isofläche mithilfe von Marching Cubes- und Surface Nets-Algorithmen extrahiert.
Ich stieß schnell auf die inhärenten Probleme bei der Darstellung des gesamten Raums mit einem 3D-Raster. Ein Großteil dieses Raums befand sich entweder über oder unter der Oberfläche, und ich wandte mich der Raumaufteilung mit Oktrees zu, da dies nicht allzu schwierig schien. Eines der großartigen Dinge an Octrees ist, dass Octree-Knoten, deren Kanten nicht von der Oberfläche geschnitten werden, nicht erneut geteilt werden müssen.
Ich recherchierte und fand ein paar Ressourcen, um meinen Code zu erstellen. Eines ist Volume GFX und ein anderes ist das Original-Dual Contouring-Papier von Ju et al. Meine Kantenprüfung erfolgt durch die Überprüfung des Marching Cube anhand von Paul Bourkes Code. Wenn der "Cubeindex" entweder 0 oder 255 ist, wird keine Kante geschnitten, und der Octree-Knoten muss nicht geteilt werden.
Mein Octree-Generierungscode funktioniert für so etwas wie eine Viertelkugel, bei der die Oberflächenmerkmale äußerst normal sind:
Wie im Bild zu sehen ist, werden nur Octree-Knoten, die die Oberfläche enthalten, unterteilt. Arbeitet großartig.
Wenn wir uns jedoch etwas Komplexerem zuwenden, wie der PerlinNoise-Funktion von Unity3d: GASP! Was macht das Loch im Netz in der unteren rechten Ecke? Bei näherer Betrachtung stellen wir fest, dass das Octree nicht richtig unterteilt wurde (rote Linien, die die Abmessungen des betreffenden Knotens hervorheben): Dies ist das gleiche Problem, das Jules Bloomenthal in ihrem Artikel über Polygonisierung , Seite 10, Abbildung 10, hervorhebt . Traditionelle Methoden zum Generieren Top-Down-Octrees ("Adaptive Subdivision") reagieren empfindlich auf feine Oberflächenmerkmale im Verhältnis zur Größe des Octree-Knotens.
Das Wesentliche:
X-------X
| |
| | <- Node
X--/\---X X's - tested values, all outside of the surface!
/ \ <- surface
Die Oberfläche bricht die Oberfläche, kommt aber wieder herunter, bevor sie über einen Scheitelpunkt geht. Da wir Kantenschnittpunkte berechnen, indem wir die Zeichen an den beiden Eckpunkten betrachten, wird die Kante dadurch nicht als gekreuzt gekennzeichnet.
Gibt es eine Methode, um festzustellen, ob diese Anomalien vorliegen? Vorzugsweise funktioniert die Lösung nicht nur für die Unity3d-Rauschfunktion, sondern für jede 3D-Rauschfunktion (für Klippen / Überhänge / schwimmende Inseln usw.).
AKTUALISIEREN
Dank Jasons großartiger Antwort konnte ich meine eigenen Fragen beantworten (in den Kommentaren unter seiner Antwort). Das Problem, das ich hatte, war, dass ich aufgrund ihrer periodischen Natur nicht verstand, wie ich eine Begrenzungsfunktion für trigonometrische Funktionen (Sinus, Cosinus usw.) erstellen konnte.
Für diese Leute ist die Periodizität der Schlüssel zu Grenzfunktionen. Wir wissen, dass sin / cos ihre Extremwerte in einem festgelegten Intervall erreichen, insbesondere in jedem π/2
. Wenn also das Intervall, das wir überprüfen, ein Vielfaches (cos) oder ein halbes Vielfaches (sin) als 1 / -1 enthält, ist dies das bestimmte Extrem. Wenn es kein Vielfaches enthält (dh das Intervall [0.1,0.2]
), entspricht der Bereich einfach den Werten der Funktion, die an ihren Endpunkten ausgewertet wird.
UPDATE 2:
Wenn das oben Gesagte keinen Sinn ergibt, überprüfen Sie Jasons Antwort auf meine Kommentare.