J'ai implémenté une interprétation approximative de l' algorithme Diamond-Square en C ++ pour créer un terrain fractal semi-réaliste, mais la sortie semble être une valeur y aléatoire à chaque point plutôt que des formes rocheuses lisses. J'ai modifié des paramètres, mais j'ai l'impression qu'un examen extérieur du code pourrait m'aider à comprendre le problème. Voici des exemples de sortie:
En tant que bitmap (de haut en bas) avec variation de hauteur réduite:
À quoi cela devrait ressembler (il est chargé à partir d'un fichier):
Le code:
//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);
}
}
}
c++
procedural-generation
terrain
heightmap
Joe Parker
la source
la source
roughness *= 0.75f;
?Réponses:
Je pense qu'en général, vous incluriez la hauteur du point central dans l'étape carrée (et faites d'abord l'étape de diamant. Qui) affecterait légèrement la façon dont elle semble épineuse, ce qui en fait une pente plus progressive. L'avez-vous essayé avec le décalage aléatoire diminué?
Il semble également que tant que les hauteurs sont positives, il n'y a aucune chance que le décalage de hauteur soit négatif, donc plus les points sont élevés, plus le décalage est élevé, ce qui le rend plus épineux.
J'ai fait un programme assez simple avec cet algoritihm donnant des résultats corrects et au lieu de baser le décalage aléatoire sur la moyenne des hauteurs, je l'ai rendu affecté par la largeur de grille actuelle.
la source
Pour corriger l'aléatoire de la hauteur, vous pouvez implémenter Perlin Noise .
Quelle hauteur de génération basée sur les hauteurs ajustées et donc vous avez obtenu des résultats très lisses.
Voici quelques implémentations sur C ++
la source