Đầu ra thuật toán Diamond-Square là ngẫu nhiên và ồn ào


8

Tôi đã thực hiện một giải thích sơ bộ về thuật toán Diamond-Square trong C ++ để tạo ra một số địa hình fractal bán thực tế, nhưng đầu ra có vẻ giống như một giá trị y ngẫu nhiên tại mỗi điểm thay vì hình dạng đá trơn tru. Tôi đã thay đổi các tham số nhưng cảm thấy như một cái nhìn bên ngoài về mã có thể giúp tôi hiểu vấn đề. Dưới đây là ví dụ về đầu ra:

Là một bitmap (từ trên xuống) với sự thay đổi chiều cao được hạ xuống:

Những gì nó nên trông giống như (điều này được tải từ một tập tin):

Mật mã:

//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
Trong phương thức diamondSapes, Bước kim cương và Bước vuông dường như hoạt động trên cùng một góc. Nhưng thực tế, bạn phải thực hiện bước vuông bốn lần, một lần cho mỗi ô vuông được tạo bởi bước kim cương trước đó. Và sau đó bước vuông sẽ làm tương tự và thực hiện bốn bước kim cương. Nhưng có khá nhiều thứ khác có mùi trong mã đó, như vòng lặp for trong diamondStep sẽ loại bỏ và viết lại giá trị trả về của hàm trong mỗi lần lặp.
Philipp

4
Khi tôi triển khai DS lần đầu tiên, tôi đã đảm bảo làm cho quá trình tương tác để tôi có thể thấy chính xác những gì đang xảy ra ở mỗi bước, bắt đầu với bốn góc của toàn bộ không gian và thực hiện theo từng bước lặp tiếp theo. Sửa đổi dữ liệu, sửa đổi các đỉnh cho phù hợp, rửa sạch, lặp lại. Tôi đề nghị bạn làm điều này, vì các thuật toán đệ quy có thể khó theo dõi.
Kỹ sư

Làm thế nào bạn quyết định giảm kích thước bước bằng roughness *= 0.75f;?
Roflo

Tôi cần phải sửa nhận xét trước đây của mình: Bạn chỉ được yêu cầu thực hiện một bước kim cương cho mỗi bước vuông chứ không phải bốn . Nhưng bạn vẫn phải thực hiện bốn bước vuông sau mỗi bước kim cương. Tôi mong đợi một triển khai thích hợp để có diamondStep gọi SquareStep và sau đó SquareStep gọi diamondStep cho đến khi đạt được độ sâu lặp mong muốn.
Philipp

Câu trả lời:


1

Tôi nghĩ thông thường bạn sẽ bao gồm chiều cao của điểm giữa trong bước vuông (và thực hiện bước kim cương trước. Điều này) sẽ ảnh hưởng một chút đến mức độ nhọn của nó làm cho nó có độ dốc dần dần. Bạn đã thử nó với phần bù ngẫu nhiên giảm chưa?

Có vẻ như chừng nào độ cao là dương thì không có cơ hội nào cho độ cao bù là âm nên điểm càng cao thì độ bù càng cao làm cho nó nhọn hơn.

Tôi đã thực hiện một chương trình khá đơn giản với algoritihm này cho kết quả ổn và thay vì dựa vào phần bù ngẫu nhiên ngoài mức trung bình của độ cao, tôi đã làm cho nó bị ảnh hưởng bởi băng thông lưới hiện tại.


-3

Để khắc phục sự ngẫu nhiên về chiều cao, bạn có thể thực hiện Perlin noise .

Việc tạo chiều cao dựa trên chiều cao tùy ý và do đó bạn có kết quả rất trơn tru.

Đây là một số triển khai trên C ++


2
Đây không phải là một câu trả lời cho câu hỏi
Bálint

1
Chào mừng bạn đến với gamedev.SE. Xin lưu ý rằng đây là một câu hỏi và câu trả lời cộng đồng. Chúng tôi chỉ trả lời các câu hỏi khi chúng được viết. Câu hỏi này rõ ràng là hỏi về một vấn đề với việc thực hiện thuật toán hình vuông kim cương. "Sử dụng một thuật toán hoàn toàn khác" không phải là một câu trả lời hay cho câu hỏi như vậy.
Philipp

1
Thuật toán Diamond Square và Perlin Noise là hai thuật toán khác nhau để tạo ra nhiễu kết hợp. Bạn sẽ không sử dụng cái này để tạo cái kia.
MichaelHouse
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.