Алгоритм-квадрат алгоритм случайный и шумный

8

Я реализовал грубую интерпретацию алгоритма Алмазного квадрата в C ++, чтобы создать некоторую полуреалистичную фрактальную местность, но результат просто выглядит как случайное значение y в каждой точке, а не как гладкие каменистые формы. Я изменил параметры, но чувствую, что внешний взгляд на код может помочь мне понять проблему. Вот примеры вывода:

Как растровое изображение (сверху вниз) с пониженной вариацией высоты:

Как это должно выглядеть (это загружается из файла):

Код:

//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
В методе diamondSquare кажется, что diamond-Step и square-Step работают в одинаковых углах. Но на самом деле вы должны выполнить квадратный шаг четыре раза, по одному для каждого из квадратов, сгенерированных предыдущим алмазным шагом. И тогда квадратный шаг должен сделать то же самое и выполнить четыре алмазных шага. Но есть много других вещей, которые пахнут в этом коде, например, цикл for в diamondStep, который отбрасывает и перезаписывает возвращаемое значение функции в каждой итерации.
Филипп
4
Когда я впервые внедрил DS, я убедился, что процесс стал интерактивным, чтобы я мог точно видеть, что происходит на каждом шаге, начиная с четырех углов всего пространства и прорабатывая каждую последующую итерацию. Измените данные, измените вершины соответственно, промойте, повторите. Я предлагаю вам сделать это, так как в противном случае рекурсивные алгоритмы могут быть трудно отследить.
инженер
Как вы решили уменьшить размер шага roughness *= 0.75f;?
Roflo
Мне нужно исправить мой предыдущий комментарий: вы должны выполнять только один шаг алмаза за каждый квадратный шаг, а не четыре . Но вы все равно должны выполнять четыре квадратных шага после каждого алмазного шага. Я ожидал бы, что правильная реализация будет иметь вызов diamondStep squareStep, а затем вызовет squareStep diamondStep, пока не будет достигнута желаемая глубина итерации.
Филипп

Ответы:

1

Я думаю, что обычно вы включаете высоту средней точки в квадратный шаг (и сначала делаете шаг алмаза, который), что немного повлияет на то, как оно выглядит колючим, делая его более плавным наклоном. Вы пробовали это с уменьшенным случайным смещением?

Также кажется, что до тех пор, пока высоты положительны, нет вероятности того, что смещение высоты будет отрицательным, поэтому, чем выше точки, тем выше смещение, делая его более резким.

Я сделал довольно простую программу с этим алгоритмом, дающим хорошие результаты, и вместо того, чтобы основывать случайное смещение от среднего значения высот, я сделал так, чтобы на него влияла текущая ширина сетки.

Бен Бизли
источник
-3

Чтобы исправить случайность высоты вы можете реализовать Perlin Noise .

Который генерирует высоту в зависимости от высоты прилегания и, таким образом, вы получили очень плавные результаты.

Вот некоторые реализации на C ++

Ashe23
источник
2
Это не ответ на вопрос
Bálint
1
Добро пожаловать в gamedev.SE. Обратите внимание, что это сообщество вопросов и ответов. Мы отвечаем только на вопросы, как они были написаны. Этот вопрос явно задает вопрос о реализации алгоритма алмазного квадрата. «Используйте совершенно другой алгоритм» не является хорошим ответом на такой вопрос.
Филипп
1
Алмазный квадрат и Perlin Noise - два разных алгоритма генерации когерентного шума. Вы не будете использовать один для создания другого.
MichaelHouse