2016-04-27 3 views
1

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

Я получил это:

Example

И должен выглядеть следующим образом:

What it *should* look like (this is loaded from a file)

Код:

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

}

+2

Что значит «грубая интерпретация»? Возможно, ваш пример - грубая интерпретация того, как он должен выглядеть. – user463035818

+0

Слишком ленив, чтобы пройти через ваш код, поэтому просто намеки: 1. Рекурсия действительно сложна в этом из-за совпадений и нерегулярностей пересечения кромок, проще/быстрее/лучше использовать итерацию, а вместо этого увидеть мой [Итеративный алмаз и квадрат в C++] (http: // stackoverflow.com/a/36258843/2521214) 2. Похоже, вы не корректируете случайность (величина должна любоваться слоем рекурсии) или не использовать среднее положение в качестве базы (смещение средней точки), но это просто визуальное впечатление, которое вам нужно проверить (может быть, и неправильная рекурсия). Также посмотрите на [генератор острова] (http://stackoverflow.com/a/36647622/2521214) – Spektre

ответ

0

Ваши расчеты средней точки выглядят неправильно. Алгоритм должен установить середину этого квадрата или алмаза как среднее из четырех угловых точек плюс случайное значение. Но в своем коде я вижу это:

y = rand() % (int)(avg/5);

В основном это говорит, установите у на случайное число между нулем & одна пятая часть в среднем по углам. Вместо этого я бы ожидать, чтобы увидеть что-то вроде этого:

// get a random float in the range 0 to 1 
float r = (float)(rand())/(float)(RAND_MAX); 
// scale r to be in the range -magnitude to magnitude 
r = (r * 2 * magnitude) - magnitude; 
// add the scaled random value to the average of the corners 
y = avg + r; 

Примечание: возможно, что такой подход даст вам у значения меньше нуля. Если это проблема, нормализуйте данные, найдя самое низкое значение &, вычитая эту сумму из каждого y.