Die Ausgabe des Diamond-Square-Algorithmus ist zufällig und verrauscht

8

Ich habe eine grobe Interpretation des Diamond-Square-Algorithmus in C ++ implementiert , um ein semi-realistisches fraktales Terrain zu erstellen, aber die Ausgabe scheint nur ein zufälliger y-Wert an jedem Punkt zu sein und keine glatten Felsformen. Ich habe die Parameter geändert, habe aber das Gefühl, dass ein Blick von außen auf den Code mir helfen könnte, das Problem zu verstehen. Hier sind Beispiele für die Ausgabe:

Als Bitmap (von oben nach unten) mit verringerter Höhenänderung:

Wie es aussehen sollte (dies wird aus einer Datei geladen):

Der Code:

//Diamond-square algorithm
HeightMap::HeightMap(float maxY) {
//type = GL_POINTS; 
//type = GL_LINES;
numVertices = RAW_WIDTH*RAW_HEIGHT; //256^2 squares => 257^2 vertices
numIndices = (RAW_WIDTH - 1)*(RAW_HEIGHT - 1) * 6; //each square is 2 triangles (6 indices)
vertices = new Vector3[numVertices];
textureCoords = new Vector2[numVertices];
indices = new GLuint[numIndices];
colours = new Vector4[numVertices];

int cornerA, cornerB, cornerC, cornerD; //Identify corners
cornerA = 0;
cornerB = RAW_WIDTH - 1;
cornerC = RAW_WIDTH*RAW_HEIGHT - RAW_WIDTH;
cornerD = RAW_WIDTH*RAW_HEIGHT - 1;

//Create vertices
for (int x = 0; x < RAW_WIDTH; ++x) {
    for (int z = 0; z < RAW_HEIGHT; ++z) {
        int offset = (x * RAW_WIDTH) + z;
        float y = 0; //Start with vertices set flat
        if (offset == cornerA ||
            offset == cornerB ||
            offset == cornerC ||
            offset == cornerD) {
            vertices[offset] = Vector3(x * HEIGHTMAP_X, maxY/2, z * HEIGHTMAP_Z); //Initialise corners to mid height
            std::cout << "Corners: " << offset << std::endl;
        }

        if (vertices[offset] == Vector3(0, 0, 0)) {
            vertices[offset] = Vector3(x * HEIGHTMAP_X, y * HEIGHTMAP_Y, z * HEIGHTMAP_Z);
        }
        //  textureCoords[offset] = Vector2(x * HEIGHTMAP_TEX_X, z * HEIGHTMAP_TEX_Z);
    }
}

Vector3 tl, tr, bl, br;
tl = vertices[cornerA];
tr = vertices[cornerB];
bl = vertices[cornerC];
br = vertices[cornerD];

float roughness = 1.0f;

Square square = Square(tl, tr, bl, br);
diamondSquare(vertices, numVertices, square, roughness);

//Colour
for (int x = 0; x < RAW_WIDTH; ++x) {
    for (int z = 0; z < RAW_HEIGHT; ++z) {
        int offset = (x*RAW_WIDTH) + z;
        float shade;
        if (vertices[offset].y > 0) {
            shade = 1 - 1.0f / (vertices[offset].y / maxY * 2);
        }
        else {
            shade = 0.1f;
        }
        colours[offset] = Vector4(shade, shade, shade, 1.0f);
        //Colour any vertex that hasn't been passed over red
        if (vertices[offset].y == maxY / 2 + 100) {
            colours[offset] = Vector4(1, 0, 0, 1);
        }
    }
}

//Create indices
numIndices = 0;
for (int x = 0; x < RAW_WIDTH - 1; ++x) {
    for (int z = 0; z < RAW_HEIGHT - 1; ++z) {
        int a = (x*(RAW_WIDTH)) + z;
        int b = ((x + 1)*(RAW_WIDTH)) + z;
        int c = ((x + 1)*(RAW_WIDTH)) + (z + 1);
        int d = (x*(RAW_WIDTH)) + (z + 1);

        indices[numIndices++] = c;
        indices[numIndices++] = b;
        indices[numIndices++] = a;
        indices[numIndices++] = a;
        indices[numIndices++] = d;
        indices[numIndices++] = c;

    }
}
BufferData();

}}

void HeightMap::squareStep(Vector3 vertices[], int len, Vector3 tl, Vector3 tr, Vector3 bl, Vector3 br, float mid, float roughness) {
for (int i = 0; i < len; i++) {
    Vector3 top = (tl + tr) / 2;
    Vector3 bot = (bl + br) / 2;
    Vector3 left = (tl + bl) / 2;
    Vector3 right = (tr + br) / 2;
    top.y = 0;
    bot.y = 0;
    left.y = 0;
    right.y = 0;
    if (vertices[i] == top ||
        vertices[i] == bot ||
        vertices[i] == left ||
        vertices[i] == right) {
        float y = rand() % (int)(mid/5);
        y *= roughness;
        vertices[i] = Vector3(vertices[i].x, mid + y, vertices[i].z); //Set Diamond centre points to mid height + rand
        std::cout << "Square: " << vertices[i];
    }
}

}}

float HeightMap::diamondStep(Vector3 vertices[], int len, Vector3 tl, Vector3 tr, Vector3 bl, Vector3 br, float roughness) {
float avg;
float y;
    for (int i = 0; i < len; i++) {
        Vector3 corners = (tl + tr + bl + br) / 4;
        avg = corners.y;
        y = rand() % (int)(avg/5);
        y *= roughness;
        corners.y = 0;
        if (vertices[i] == corners) {
            vertices[i] = Vector3(vertices[i].x, avg + y, vertices[i].z);         //Set Square centre point to avg height of corners + rand
            std::cout << "Diamond: " << vertices[i];
        }
    }
return avg + y;

}}

void HeightMap::diamondSquare(Vector3 vertices[], int numVertices, Square s, float roughness) {
Vector3 tl = s.tl;
Vector3 tr = s.tr;
Vector3 bl = s.bl;
Vector3 br = s.br;
float mid = diamondStep(vertices, numVertices, tl, tr, bl, br, roughness);
squareStep(vertices, numVertices, tl, tr, bl, br, mid, roughness);
roughness *= 0.75f;
if (s.width > 2 * HEIGHTMAP_X) {
    std::vector<Square> squares = s.split();
    for (int i = 0; i < 4; i++) {
        diamondSquare(vertices, numVertices, squares[i], roughness);
    }
}

}}

Joe Parker
quelle
1
Bei der Methode diamantSquare scheinen der Diamantschritt und der Quadratschritt an denselben Ecken zu arbeiten. Aber eigentlich solltest du den Quadratschritt viermal ausführen, einmal für jedes der Teilquadrate, die durch den vorherigen Diamantschritt erzeugt wurden. Und dann sollte der quadratische Schritt dasselbe tun und vier Diamantschritte ausführen. Aber es gibt noch viele andere Dinge, die in diesem Code riechen, wie die for-Schleife in diamantStep, die den Rückgabewert der Funktion in jeder Iteration verwirft und neu schreibt.
Philipp
4
Als ich DS zum ersten Mal implementierte, stellte ich sicher, dass der Prozess interaktiv war, damit ich genau sehen konnte, was bei jedem Schritt geschah, angefangen bei den vier Ecken des gesamten Raums bis hin zu jeder nachfolgenden Iteration. Daten ändern, Scheitelpunkte entsprechend ändern, spülen, wiederholen. Ich schlage vor, dass Sie dies tun, da es ansonsten schwierig sein kann, den Überblick über rekursive Algorithmen zu behalten.
Ingenieur
Wie haben Sie beschlossen, die Schrittweite um zu verringern roughness *= 0.75f;?
Roflo
Ich muss meinen vorherigen Kommentar korrigieren: Sie sollten nur einen Diamantschritt für jeden Quadratschritt ausführen , nicht vier . Aber Sie sollten immer noch vier quadratische Schritte nach jedem Diamantschritt ausführen. Ich würde erwarten, dass bei einer ordnungsgemäßen Implementierung diamantStep squareStep und dann squareStep diamantStep aufruft, bis die gewünschte Iterationstiefe erreicht ist.
Philipp

Antworten:

1

Ich denke, normalerweise würden Sie die Höhe des Mittelpunkts in die quadratische Stufe einbeziehen (und zuerst die Diamantstufe ausführen), was sich leicht darauf auswirken würde, wie stachelig es aussieht, was es zu einer allmählicheren Neigung macht. Haben Sie es mit verringertem zufälligen Versatz versucht?

Es scheint auch, dass solange die Höhen positiv sind, es keine Chance gibt, dass der Höhenversatz negativ ist. Je höher die Punkte, desto höher der Versatz, wodurch er stacheliger wird.

Ich habe ein ziemlich einfaches Programm mit diesem Algorithmus erstellt, das gute Ergebnisse liefert, und anstatt den zufälligen Versatz auf den Durchschnitt der Höhen zu stützen, habe ich ihn durch die aktuelle Gitterbreite beeinflusst.

Ben Beazley
quelle
-3

Um die Zufälligkeit der Höhe zu korrigieren, können Sie Perlin Noise implementieren .

Welche Erzeugungshöhe basiert auf den angrenzenden Höhen und somit erhalten Sie sehr gleichmäßige Ergebnisse.

Hier ist eine Implementierung in C ++

Ashe23
quelle
2
Dies ist keine Antwort auf die Frage
Bálint
1
Willkommen bei gamedev.SE. Bitte beachten Sie, dass dies eine Frage-Antwort-Community ist. Wir beantworten Fragen nur so, wie sie geschrieben wurden. Diese Frage bezieht sich explizit auf ein Problem bei der Implementierung des Diamant-Quadrat-Algorithmus. "Verwenden Sie einen völlig anderen Algorithmus" ist keine gute Antwort auf eine solche Frage.
Philipp
1
Diamond Square-Algorithmus und Perlin Noise sind zwei verschiedene Algorithmen zur Erzeugung von kohärentem Rauschen. Sie würden nicht eins verwenden, um das andere zu erstellen.
MichaelHouse