# Generate terrain once given the heightmap

This topic is 749 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

## Recommended Posts

Hi at all! I'm experimenting with OpenGL. I'm having a lot of "fun" XD.

Anyway, I'm trying to generate a set of triangles, which all together build the terrain.
Given a bitmap, the algorithm iterate through all the pixels and calculate the X,Y position, "normalize" the height and then multiply it for the max height you need.

This is the heightmap I'm trying to use:

This is the code to generate a set of triangles:

// Generate terrain
Model * GenerateTerrain(const char * name, float width, float height, float up)
{
// Allocate the model on the heap
Model * model = new Model();

// Load the actual heightmap from file

// Build the model with vertices and UV mapping
for (unsigned int i = 0; i != img.imageSize - img.width * 3; i += 3)
{
// Get coords data and color
unsigned int pos = (i / 3);
float x = pos % img.width;
float y = floor(pos / img.height);
float z[] = { (float)img.data[i] / 255.0f , (float)img.data[i + img.width * 3] / 255.0f };

// Build the first triangle
model->vertices.push_back(glm::vec3(x, y, z[0] * up));
model->vertices.push_back(glm::vec3(x + 1.0f, y, z[0] * up));
model->vertices.push_back(glm::vec3(x + 1.0f, y + 1.0f, z[1] * up));

// Build the first triangle
model->vertices.push_back(glm::vec3(x + 1.0f, y + 1.0f, z[1] * up));
model->vertices.push_back(glm::vec3(x, y + 1.0f, z[1] * up));
model->vertices.push_back(glm::vec3(x, y, z[0] * up));

// Calculate UV mapping (this is a placeholder)
model->uvs.push_back(glm::vec2(x, y));
model->uvs.push_back(glm::vec2(x + 1.0f, y + 1.0f));
}

// Resize the model if requested
if (width != 0.0f && height != 0.0f)
for (unsigned int i = 0; i != model->vertices.size(); i++)
{
model->vertices[i].x = model->vertices[i].x / img.width * width;
model->vertices[i].y = model->vertices[i].y / img.height * height;
}

// Return the pointer to the model
return model;
}


I iterate by a "step of three" (i += 3), since a heightmap is a grayscale image, so every component (R, G or B) of a pixel will be the same.

ImageBMP is a data structure which contains header info (width, height and size) and a data pointer (which points to the begin of a set of bytes that represent RGB components).

X and Y positions seem to be right, but not Z.
This is what I get:

Laterally, I get some holes. I tried getting the "height" of the below pixel (this is why I iterate through all the pixels, except the ones which make the last line of the bitmap), but I still have holes.

So, I guess I'm missing something. Do you know what? This is the first time I got into OpenGL. I succeded into making a "resource system" (which loads all the needed resources, so they are not loaded more times if they are requested), some dirty keyboard controls and a very nice camera rotation by mouse movement.

Now, I'm stuck with this... I tried using GL_TRIANGLE_STRIP, but then the model appears to be "stomped" (I cannot explain, but it's weird) and closed using the heightmap above.

Edited by FonzTech

##### Share on other sites

Hi,

It can be hard to tell if your calculations are a bit off (cant do them in my head :) ) or what affect the 'up' value is having against your z value. But id find something like the following a little easier, altough not tested

float scale = 10.f;
float height = (float)img.data / 255.0f;

for( int z = 0; z < img.height; z++ )
{
for( int x = 0; z < img.width; x++ )
{
model->vertices.push_back(glm::vec3(  x    * scale, height * scale,  z    * scale ));
model->vertices.push_back(glm::vec3( (x+1) * scale, height * scale,  z    * scale ));
model->vertices.push_back(glm::vec3( (x+1) * scale, height * scale, (z+1) * scale ));

model->vertices.push_back(glm::vec3(  x    * scale, height * scale,  z    * scale ));
model->vertices.push_back(glm::vec3(  x    * scale, height * scale, (z+1) * scale ));
model->vertices.push_back(glm::vec3( (z+1) * scale, height * scale, (z+1) * scale ));
}
}

As for ruling out problems with your own, try slap glPolygonMode( GL_FRONT, GL_LINE ); above your rendering, and it will display it as a wiremesh - you should then atleast see how your triangles look beneath the surface.

Maybe just try drawing it all using points first - get the calculations correct - and then move onto triangles when you are happy. you can make the point size bigger using glPointSize(5.f);

Sorry i cant help anymore at the minute, but keep at it!

##### Share on other sites

It looks like you're only using two z positions for each pair of triangles. I'm not sure how that's supposed to work... each corner would have a different height, so there should be 4 samples of the heightmap.

Not to mention you're only pushing 1/3 of the uvs as you are vertex positions into your model.

##### Share on other sites

I managed to get a decent terrain generation with UV mapping, although the model seems to be a bit "pixellated" to me XD.

// Build the model with vertices and UV mapping
for (unsigned int i = 0; i < img.width - 1; i++)
for (unsigned int j = 0; j < img.height - 1; j++)
{
// Get the index in the bitmap data
unsigned int index = i * 3 + j * 3 * img.width;

// Calculate vertices data
float x = (float)i;
float y = (float)j;
float z[] = { (float)img.data[index] / 255.0f , (float)img.data[index + 3] / 255.0f, (float)img.data[index + img.width * 3] / 255.0f , (float)img.data[index + img.width * 3 + 3] / 255.0f };

// Calculate UV mapping
float u[] = { x / (float)img.width , (x + 1.0f) / (float)img.width };
float v[] = { y / (float)img.height , (y + 1.0f) / (float)img.height };

// Build the first triangle
model->vertices.push_back(glm::vec3(x, y, z[0] * up));
model->vertices.push_back(glm::vec3(x + 1.0f, y, z[1] * up));
model->vertices.push_back(glm::vec3(x + 1.0f, y + 1.0f, z[3] * up));

model->uvs.push_back(glm::vec2(u[0], v[0]));
model->uvs.push_back(glm::vec2(u[1], v[0]));
model->uvs.push_back(glm::vec2(u[1], v[1]));

// Build the second triangle
model->vertices.push_back(glm::vec3(x, y, z[0] * up));
model->vertices.push_back(glm::vec3(x, y + 1.0f, z[2] * up));
model->vertices.push_back(glm::vec3(x + 1.0f, y + 1.0f, z[3] * up));

model->uvs.push_back(glm::vec2(u[0], v[0]));
model->uvs.push_back(glm::vec2(u[0], v[1]));
model->uvs.push_back(glm::vec2(u[1], v[1]));
}


##### Share on other sites

The pixelation is probably to do with your texture setup, have a look here at the section describing GL_NEAREAST and GL_LINEAR http://learnopengl.com/#!Getting-started/Textures'>http://learnopengl.com/#!Getting-started/Textures ( or just at the whole site in general, its a great resource :) )

Edited by McGrane

##### Share on other sites

The pixelation is probably to do with your texture setup, have a look here at the section describing GL_NEAREAST and GL_LINEAR

I was talking about the 3D model itself, not the texture. It appears to be a bit "jagged"... then I realized that it's obvious, because I'm generating a 3D model from a grayscale bitmap (considering 8 bit per pixel isn't that much XD). Once textured properly it doesn't appear that bad :P

Anyway, thanks to all for all the help given!

##### Share on other sites

Regarding the jaggedness, there are two ways to triangulate each grid cell. If you choose the one that has the center "axis" parallel to the gentlest slope of the quad, your terrain will come out a little smoother (especially if you're using normals for lighting).

https://mtnphil.wordpress.com/2011/09/22/terrain-engine/

Since you're doing this on the CPU, it's easy enough to do as you can just re-order the triangle vertices as you create them. But if you end up doing it in a vertex shader at some point:

https://mtnphil.wordpress.com/2012/10/15/terrain-triangulation-summary/

1. 1
2. 2
Rutin
21
3. 3
JoeJ
18
4. 4
5. 5

• 14
• 39
• 23
• 13
• 13
• ### Forum Statistics

• Total Topics
631717
• Total Posts
3001877
×