Glattes Voxel-Terrain

13

Als persönliches Projekt versuche ich, einen Geländegenerator zu entwickeln, der ein Gelände erzeugt, das dem glatten Gelände von Castle Story ähnelt.

Wenn Sie es noch nicht gesehen haben, hier: Bildbeschreibung hier eingeben

Sie sehen also, dass es sich um eine Kombination aus Blöcken und "glatten" Blöcken handelt.

Um diesen Look zu emulieren, habe ich versucht, jedem Oberflächenblock eine Mini-Höhenkarte zu geben. Dies funktioniert im Allgemeinen, aber es gibt einige Probleme, die ein Gelände wie dieses ergeben:

Bildbeschreibung hier eingeben

Das Problem ist, dass jeder Block 1x1x1 ist, aber manchmal ist die Höhe an einer bestimmten Stelle negativ oder> 1. In diesem Fall beschneide ich ihn und setze die Höhe auf 0 oder 1.

Um besser zu veranschaulichen, was ich meine, ist hier ein Diagramm: Bildbeschreibung hier eingeben

Um die Höhe zu generieren, mache ich im Grunde:

genColumn(int x, int z)
{
    int highestBlockY = (int)noise2d(x, z);

    bool is_surface = true;

    for(int y = max_height - 1; y >= 0; y--)
    {
        Block b;

        if(is_surface)
        {
            b = Block.Grass;
            b.HasHeightMap = true;

            // generate heightmap
            for(int ix = 0; ix < 5; ix++)
            {
                for(int iz = 0; iz < 5; iz++)
                {
                    float heightHere = noise2d(x + ix / 4, z + iz / 4) - y;

                    // clip heights
                    if(heightHere > 1)
                        heightHere = 1;

                    if(heightHere < 0)
                        heightHere = 0;

                    b.HeightMap[ix][iz] = heightHere;
                }
            }

            is_surface = false;
        }
        else
        {
            b = Block.Dirt;
        }

        setBlock(x, y, z, b);
    }
}

Vielleicht gehe ich das falsch an, indem ich den "wahren" Perlin-Rauschwert benutze?

Jede Hilfe wäre sehr dankbar!

ohne Titel
quelle

Antworten:

11

Castle Story sieht aus technischen Gründen so aus: Würde es eine Höhenkarte pro Voxel im gesamten Volumen geben, und nicht nur eine Höhenkarte pro Oberflächenvoxel , wären die Speicherkosten in der Größenordnung von O (n ^ 3 erheblich höher ), die unerschwinglich sein kann, im Gegensatz zu einem günstigeren O (n ^ 2), wobei n die Seitenlänge eines kubischen Voxelraums ist, der Ihre Welt darstellt. Beachten Sie, dass Höhenkarteninformationen für unterirdische Voxel in der Gitterstruktur impliziert sind, sodass Höhenkarteninformationen nur für die Voxel explizit gespeichert werden müssen, die auf der Oberfläche liegen. Also haben die Jungs von Castle Story Eckpunkte an Gitterzwischenräumen ausgerichtet, um Speicherkosten und Komplexität bei der Netzkonstruktion zu sparen ... lesen Sie weiter.

Schauen wir uns zunächst Ihre Optionen an:

Bildbeschreibung hier eingeben

(1) wird Ihnen die Sache erschweren, da Sie in einer einzelnen Voxel-Spalte nur Mini-Heightmap-Informationen für das oberste Voxel wünschen - lesen Sie den ersten Absatz, um zu erfahren, warum. Unter (1) finden Sie in der rechten Spalte Informationen zur Höhenkarte sowohl für die oberste als auch für die zweithöchste Position (und dies könnte auch für die dritthöchste Position usw. gelten, wenn die Steigung extrem genug wäre.). Das ist nicht gut.

(2) Ist wahrscheinlich eine bessere Option als; Dies bedeutet, dass die Eckpunkte an den Voxelgitterzwischenräumen einrasten. Aber wie gehen wir dann mit dem Problem der extremen Neigung um? Nun, wir müssen einen Verlauf auswählen, bei dem wir einfach an einer vertikalen Spalte einrasten und so sicherstellen, dass wir keine Verläufe haben, die durch n oberste Voxel verlaufen können. Ein 45-Grad-Gradient ist die natürliche Grenze aus Gründen, die ich unten erläutere. Anstelle von (3) hätten wir also (4):

Bildbeschreibung hier eingeben

(4) Die Lösung besteht darin, den Eckpunkt des Voxels auf den nächsten Gitterzwischenraum zu richten. Der visuelle Effekt - diagonales Aliasing - ist in Ihrem Screenshot von Castle Story zu sehen. Die Cutoff-Steigung für die Voxel beträgt 1: 1 oder 45 Grad (orthogonal gesehen). Schauen wir uns die Gründe für die Lösung an:

  • Der einzige Weg, wie wir einen ungebrochenen, extremen Hang haben könnten, wäre, wenn ein Voxel vertikal verlängert wäre ...
  • .... Aber kein Voxel-Netz kann seinen Begrenzungsbereich überschreiten, unabhängig von seiner tatsächlich geglätteten Form. Ein Voxel muss in einem definierten Raum in einem 3D-Gitter sitzen, das, wenn nicht für seine exakte Form, dann zumindest für seine achsenausgerichtete Begrenzungsbox verantwortlich ist.

Ein weiterer Grund für diese Vorgehensweise besteht darin, dass, wie Sie bereits festgestellt haben, das Verzichten auf das Einrasten (Diskretisieren) zu einer Reihe geometrisch komplexer Oberflächenglättungsszenarien führt, die insgesamt am besten vermieden werden können ... Spiele dieser Art erfordern im Allgemeinen keine Der Grad an Genauigkeit, den ein korrekter CSG-Algorithmus bieten würde. Dies ist der ganze Grund, warum wir Voxel in erster Linie verwenden: Mit Voxeln ist es weitaus einfacher, inkrementell mit Volumina zu arbeiten, als mit Gleitkomma-Algorithmen (stetige) für Polygonkreuzungen / CSG .

Ingenieur
quelle
Ich habe gerade versucht, dies zu implementieren, und bin auf einige Verwirrung gestoßen - was meinen Sie mit "Zwischenräumen"? Meinen Sie die ganzzahligen Gitterkoordinaten?
Ohne Titel
@ThomasBradworth. "Ein Raum, der zwischen den Dingen interveniert." Für 2D haben Sie, wenn Sie ein angstvolles Gitter von Zellen haben, (n + 1) x (n + 1) Zwischenräume. Dies erstreckt sich direkt auf 3D. Sie sind die "Eckpunkte", die jede Zelle begrenzen und die meistens von mehreren Zellen / Voxeln gemeinsam genutzt werden. Wenn Sie es auf 1D (eine Linie) reduzieren, dann, wenn sowohl die Zellen als auch die Zwischenräume als Array dargestellt werden, wird Zelle 0 durch Zwischenraum 0 und Zwischenraum 1 begrenzt, während Zelle 1 durch Zwischenraum 1 und Zwischenraum 2 begrenzt wird. usw.
Ingenieur
Danke für die Antwort! Ich habe genau das versucht, aber ich habe einige ungünstige Ergebnisse erzielt. Zuerst habe ich versucht , einfach die Höhenwerte an den Rändern Rundung (wenn x 0 oder 4 ist , oder z 0 oder 4), aber das hat mir: i.imgur.com/eQW7Y.png ( pastebin.com/Lr8vyyHB ) Als nächstes habe ich versucht, die Punkte in der Mitte zu glätten, also habe ich eine "Höhenfixierungsfunktion" hinzugefügt: pastebin.com/AazQ07Xm Das hat mir jedoch auch ein kantigeres, aber auch schlechtes Ergebnis gebracht: i.imgur.com/q1JVP .png Vielleicht stimmt etwas mit meiner Rauschfunktion nicht?
ohne Titel
@ThomasBradsworth Leider habe ich das Gefühl, dass Sie die Antwort, die ich über eine Stunde lang geschrieben und bearbeitet habe, nur zur Hälfte gelesen / verstanden haben. Lärm vergessen; Wenn Sie die Rauschfunktion nicht verstehen, entfernen Sie sie vorerst aus der Gleichung. Ihr Problem liegt darin, wie einzelne Voxelnetzdaten aufgebaut sind, und Rauschen hat nichts damit zu tun, da Sie ohne Rauschen eine Höhenkarte erstellen können. Arbeiten Sie mit dem einfachsten Testfall, dh codieren Sie das Höhenarray fest. Zeigen Sie dann Ihre Ergebnisse an und optimieren Sie den Algorithmus zur Netzgenerierung. Wiederholen, bis es das tut, was erwartet wird. Setzen Sie dann das Rauschen wieder ein und überprüfen Sie es.
Ingenieur
Wollen Sie damit sagen, dass ich nur die Höhenwerte für die Ecken und nicht die Höhenwerte der "Zwischen" -Punkte speichern soll?
ohne Titel