Jump to content
  • Advertisement
Sign in to follow this  
FonzTech

Generate terrain once given the heightmap

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

If you intended to correct an error in the post then please contact us.

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:

2z7mjpz.jpg

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
	ImageBMP img = ImageLoader::LoadBMP("terrains/heightmap.bmp");
	
	// 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:
 
2lsxg7o.png

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.

 

Do you have any advice?

Thanks in advance!

Edited by FonzTech

Share this post


Link to post
Share on other sites
Advertisement

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 this post


Link to post
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 this post


Link to post
Share on other sites

Thanks for all the answers.

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 this post


Link to post
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 :) )

 

edit: fixed link

edit: edit: didnt fix link

Edited by McGrane

Share this post


Link to post
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 this post


Link to post
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).

 

See "additional optimizations" here:

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/

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!