Problem generowania terenu o kwadratach diamentów

11

Zaimplementowałem algorytm kwadratu diamentowego zgodnie z tym artykułem: http://www.lighthouse3d.com/opengl/terrain/index.php?mpd2

Problem polega na tym, że te strome klify dostaję na całej mapie. Zdarza się to na krawędziach, gdy teren jest rekurencyjnie podzielony:

wprowadź opis zdjęcia tutaj

Oto źródło:

void DiamondSquare(unsigned x1,unsigned y1,unsigned x2,unsigned y2,float range)
    {      
    int c1 = (int)x2 - (int)x1;
    int c2 = (int)y2 - (int)y1;
    unsigned hx = (x2 - x1)/2;
    unsigned hy = (y2 - y1)/2;
    if((c1 <= 1) || (c2 <= 1))
            return;

// Diamond stage
float a = m_heightmap[x1][y1];
float b = m_heightmap[x2][y1];
float c = m_heightmap[x1][y2];
float d = m_heightmap[x2][y2];
float e = (a+b+c+d) / 4 + GetRnd() * range;

m_heightmap[x1 + hx][y1 + hy] = e;

// Square stage
float f = (a + c + e + e) / 4 + GetRnd() * range;
m_heightmap[x1][y1+hy] = f;
float g = (a + b + e + e) / 4 + GetRnd() * range;
m_heightmap[x1+hx][y1] = g;
float h = (b + d + e + e) / 4 + GetRnd() * range;
m_heightmap[x2][y1+hy] = h;
float i = (c + d + e + e) / 4 + GetRnd() * range;
m_heightmap[x1+hx][y2] = i;

DiamondSquare(x1, y1, x1+hx, y1+hy, range / 2.0);   // Upper left
DiamondSquare(x1+hx, y1, x2, y1+hy, range / 2.0);   // Upper right
DiamondSquare(x1, y1+hy, x1+hx, y2, range / 2.0);   // Lower left
DiamondSquare(x1+hx, y1+hy, x2, y2, range / 2.0);       // Lower right

}

Parametry: (x1, y1), (x2, y2) - współrzędne, które definiują region na mapie wysokości (domyślnie (0,0) (128 128). zasięg - w zasadzie maks. wysokość. (domyślnie 32)

Pomoc byłaby bardzo mile widziana.

kafka
źródło
Nie patrząc na kod, wygląda na to, że prawdopodobnie masz złe rogi w niewłaściwych wywołaniach w 4 wywołaniach rekurencyjnych na końcu. Mapa wygląda, jakby każdy kwadrat został obrócony / obrócony przed obliczeniem następnego zestawu, dzieląc mapę na dziwne klify. Dolna krawędź prawego górnego kwadratu wygląda tak, jakby pasowała do prawej krawędzi lewego górnego kwadratu i tak dalej.
DampeS8N
Nie jestem pewny co masz na myśli. Środek układu współrzędnych znajduje się w lewym górnym rogu, oś x wskazuje w prawo, ay w dół. Tak więc w pierwszej iteracji (x1 = 0, y1 = 0), (x2 = 128, y2 = 128) i (x1 + hx = 64, y1 + hy = 64) jest środkiem kwadratu. Kwadrat jest zatem podzielony na 4 podkwarki: ((0,0) (64,64)), ((64,0) (128,64)), ((0,64) (64 128) i ((64, 64) (128 128). Wygląda mi dobrze ...
kafka,

Odpowiedzi:

12

Na każdym poziomie podziału krok „kwadratowy” opiera się na wynikach „kroku diamentowego”. Ale ma to również wpływ na krok diamentu wytworzony w sąsiedniej komórce, którego nie rozliczasz. Przepisałbym funkcję DiamondSquare, aby iterować najpierw szerokość, a nie głębokość tak, jak ją obecnie masz.

Pierwszym problemem jest to, że ponieważ dwukrotnie obliczyłeś kwadratowe krawędzie, ignoruje on udział sąsiedniego punktu środkowego. Na przykład w artykule, do którego się odwołujesz,

P = (J + G + K + E)/4 + RAND(d)

ale twój kod działa skutecznie

P = (J + G + J + E)/4 + RAND(d)

tzn. dwukrotnie uwzględnia bieżący punkt środkowy, a nie sąsiedni punkt środkowy. Dlatego musisz przejść na szerokość, aby obliczyć poprzednie punkty środkowe.

Oto mój kod i wynik:.

void DiamondSquare(unsigned x1, unsigned y1, unsigned x2, unsigned y2, float range, unsigned level) {
    if (level < 1) return;

    // diamonds
    for (unsigned i = x1 + level; i < x2; i += level)
        for (unsigned j = y1 + level; j < y2; j += level) {
            float a = m_heightmap[i - level][j - level];
            float b = m_heightmap[i][j - level];
            float c = m_heightmap[i - level][j];
            float d = m_heightmap[i][j];
            float e = m_heightmap[i - level / 2][j - level / 2] = (a + b + c + d) / 4 + GetRnd() * range;
        }

    // squares
    for (unsigned i = x1 + 2 * level; i < x2; i += level)
        for (unsigned j = y1 + 2 * level; j < y2; j += level) {
            float a = m_heightmap[i - level][j - level];
            float b = m_heightmap[i][j - level];
            float c = m_heightmap[i - level][j];
            float d = m_heightmap[i][j];
            float e = m_heightmap[i - level / 2][j - level / 2];

            float f = m_heightmap[i - level][j - level / 2] = (a + c + e + m_heightmap[i - 3 * level / 2][j - level / 2]) / 4 + GetRnd() * range;
            float g = m_heightmap[i - level / 2][j - level] = (a + b + e + m_heightmap[i - level / 2][j - 3 * level / 2]) / 4 + GetRnd() * range;
        }

    DiamondSquare(x1, y1, x2, y2, range / 2, level / 2);
}

http://i.imgur.com/laBhN.png

Jimmy
źródło
Tak, myślałem również zgodnie z szerokim podejściem. Te fraktale zawsze powodują mi problemy. Podobnie było z hałasem Perlina i systemami L. Jesteś niesamowity.
kafka
3

Jedną z możliwości jest to, że używasz skrótu do implementacji, którego nie robi algorytm na połączonej stronie.

W przypadku kwadratu obliczasz wysokość punktów za pomocą

float f = (a + c + e + e) / 4 + GetRnd() * range;
m_heightmap[x1][y1+hy] = f;

którego algorytm strony wskazuje, że ma być używany podczas owijania mapy. Daje to wrażenie, że używasz wartości wysokości „następny kwadrat nad” do obliczenia tego. W najprostszym pierwszym przypadku punkt środkowy (o wysokości „e”) jest używany zarówno po lewej, jak i po prawej stronie do obliczenia f.

Jednak w algorytmie, do którego się odwołujesz, używasz rzeczywistych wartości innych kwadratów / diamentów, aby pomóc ci obliczyć wartość wysokości tego punktu kwadratowego. W ich algorytmie punkt drugiego poziomu jest obliczany według następującego wzoru:

N = (K + A + J + F)/4 + RAND(d)

Czy zauważysz brak duplikacji wartości?

Myślę, że powinieneś spróbować użyć nie zawijających się wersji podanych formuł. Myślę, że te będą się powtarzać lepiej.

F = (A + C + E)/3 + ...
    instead of
F = (A + C + E + E)/4 + ...
fnord
źródło
Dzięki, to była pomocna obserwacja. Myślę, że nauczyłem się mojej zmiany, by nie przeskakiwać bezpośrednio do kodowania, kiedy widzę równania.
kafka
Jesteś bardzo mile widziany. Sam też to robię dużo czasu ... „Słuchaj, coś, co mogę kodować. Muszę. Kodować. Teraz!”
fnord