L'output dell'algoritmo Diamond-Square è casuale e rumoroso


8

Ho implementato un'interpretazione approssimativa dell'algoritmo Diamond-Square in C ++ per creare un terreno frattale semi-realistico, ma l'output sembra un valore y casuale in ogni punto piuttosto che forme rocciose lisce. Ho modificato i parametri ma mi sembra che uno sguardo esterno al codice possa aiutarmi a capire il problema. Ecco alcuni esempi dell'output:

Come bitmap (dall'alto verso il basso) con variazione dell'altezza ridotta:

Come dovrebbe essere (questo viene caricato da un file):

Il codice:

//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);
    }
}

}


1
Nel metodo diamondSquare, il diamond-Step e il Square-Step sembrano operare sugli stessi angoli. Ma in realtà dovresti eseguire il passo quadrato quattro volte, una volta per ciascuno dei quadrati secondari generati dal precedente passaggio del diamante. E poi il passo quadrato dovrebbe fare lo stesso ed eseguire quattro passi di diamante. Ma ci sono molte altre cose che odorano in quel codice, come il for-loop in diamondStep che scarta e riscrive il valore di ritorno della funzione in ogni iterazione.
Philipp,

4
Quando ho implementato DS per la prima volta, mi sono assicurato di rendere interattivo il processo in modo da poter vedere esattamente cosa stava succedendo in ogni fase, iniziando dai quattro angoli dell'intero spazio e facendosi strada attraverso ogni iterazione successiva. Modifica i dati, modifica i vertici di conseguenza, risciacqua, ripeti. Ti suggerisco di farlo, poiché altrimenti gli algoritmi ricorsivi possono essere difficili da tenere traccia.
Ingegnere

Come hai deciso di ridurre la dimensione del passo di roughness *= 0.75f;?
Roflo,

Devo correggere il mio commento precedente: dovresti eseguire solo un passo di diamante per ogni passo quadrato, non quattro . Ma dovresti comunque eseguire quattro passi quadrati dopo ogni passo di diamante. Mi aspetto che un'implementazione corretta abbia diamondStep call squareStep e quindi squareStep call diamondStep fino a raggiungere la profondità di iterazione desiderata.
Philipp,

Risposte:


1

Penso che di solito includeresti l'altezza del punto medio nel gradino quadrato (e fai prima il gradino di diamante. Quale) influenzerebbe leggermente quanto appare appuntito rendendolo un pendio più graduale. L'hai provato con l'offset casuale diminuito?

Sembra anche che fino a quando le altezze sono positive non vi è alcuna possibilità che l'offset di altezza sia negativo, quindi più alti sono i punti, maggiore è l'offset che lo rende più appuntito.

Ho creato un programma abbastanza semplice con questo algoritmo che ha dato risultati soddisfacenti e invece di basare l'offset casuale sulla media delle altezze, l'ho reso influenzato dall'attuale larghezza della griglia.


-3

Per correggere la casualità in altezza è possibile implementare il rumore Perlin .

Quale altezza di generazione in base alle altezze adiacenti e quindi hai ottenuto risultati molto fluidi.

Ecco alcune implementazioni su C ++


2
Questa non è una risposta alla domanda
Bálint,

1
Benvenuto in gamedev.SE. Questa è una community di domande e risposte. Rispondiamo alle domande solo quando sono state scritte. Questa domanda pone esplicitamente un problema con l'implementazione dell'algoritmo diamond-square. "Usa un algoritmo completamente diverso" non è una buona risposta a una domanda del genere.
Philipp,

1
L'algoritmo Diamond Square e Perlin Noise sono due algoritmi diversi per generare rumore coerente. Non useresti l'uno per creare l'altro.
MichaelHouse
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.