Advertisement

My terrain contains some strange line segments

Started by September 27, 2017 07:57 PM
5 comments, last by galop1n 7 years, 4 months ago

Hey guys, I need your help again if somebody here would kindly help me out.

I recently loaded my very first terrain using a heightmap image for the first time. It worked great! However, I noticed some strange line segments that are being drawn from the top of the terrain stretching all the way down to the very bottom. I will post images of this below so you could see what I mean, I will also link you the resource I used to generate my terrain (which is really just 1 YouTube video) here:  

" rel="external" style="background-color:#fafafa;color:#5381ac;font-size:13px;text-align:left;">

I'd be very thankful if somebody could review the code with me and help me identify the problem...

Oddly enough, the person in the YouTube video seemed to have had a very similar (if not the exact same) problem I am having at 18:38 but he fixed it by adding one simple thing which I also added but didn't do the trick for me :( Also he is using the fixed function pipeline to demonstrate the technique, I am using the programmable pipeline for better performance. Anyway, here is the result of my terrain: 

Capture.thumb.jpg.dd6aa05a6c83b352989b41bf7dfebf6e.jpg
The unwanted line segments:

Capture3.thumb.JPG.7b41ab9c3e1aa7ce012d6bbd4b4c5fe1.JPGCapture2.thumb.jpg.855d9a50393411d13c2a5830281f020c.jpg

As you can see, I also have grass (very awful looking grass too :p) on top of my terrain while playing around with the geometry shader the other day.

Here is how I am loading the heightmap, and here is where I believe the problem lies: 


 

void TerrainLoader::LoadHeightmapImage(const char* file)
{
	// Load the bitmap
	m_pImage = SDL_LoadBMP(file);
	Uint32 Pixel = 0;
 
	// Check for errors
	if (m_pImage == nullptr)
	{
		std::cerr << "error: Heightmap image could not be loaded.\n";
		return;
	}
 
	// Get bitmap's width and height
	m_HeightmapHeight = m_pImage->h;
	m_HeightmapWidth = m_pImage->w;
 
	// Read the bitmap (stores in this 2D STL vector of floats: vector<vector<float> > m_vHeights;)
	std::vector<float> tmp;
	for (int i = 0; i < m_HeightmapHeight; ++i)
	{
		for (int j = 0; j < m_HeightmapWidth; ++j)
		{
			Pixel = ((Uint32*)m_pImage->pixels)[i * m_pImage->pitch / 4 + j];
			unsigned char r, g, b;
			SDL_GetRGB(Pixel, m_pImage->format, &r, &g, &b);
			tmp.push_back((float)r / 255.0);
		}
 
		m_vHeights.push_back(tmp);
		tmp.clear();
	}
 
	SDL_FreeSurface(m_pImage);
 
	float h = 0.4f, terrainSize = 0.005f;
 
	std::vector<glm::vec3> Vertices;
	std::vector<glm::vec2> Textures;
 
	for (int i = 0; i < m_vHeights.size() - 1; ++i)
	{
		for (int j = 0; j < m_vHeights.size() - 1; ++j)
		{
			// Put the vertices and texture coordinates in their respective buffers
			Textures.push_back(glm::vec2(0.0f, 0.0f));
			Vertices.push_back(glm::vec3(i * terrainSize, m_vHeights[j] * h, j * terrainSize));
 
			Textures.push_back(glm::vec2(1.0f, 0.0f));
			Vertices.push_back(glm::vec3((i + 1) * terrainSize, m_vHeights[i + 1][j] * h, j * terrainSize));
 
			Textures.push_back(glm::vec2(1.0f, 1.0f));
			Vertices.push_back(glm::vec3(i * terrainSize, m_vHeights[j + 1] * h, (j + 1) * terrainSize));
 
			Textures.push_back(glm::vec2(0.0f, 1.0f));
			Vertices.push_back(glm::vec3((i + 1) * terrainSize, m_vHeights[i + 1][j + 1] * h, (j + 1) * terrainSize));
		}
	}
 
	// Send the data to the GPU
	glGenVertexArrays(1, &m_VAO);
	glBindVertexArray(m_VAO);
 
	glGenBuffers(1, &m_VBO[0]);
	glBindBuffer(GL_ARRAY_BUFFER, m_VBO[0]);
	glBufferData(GL_ARRAY_BUFFER, Vertices.size() * sizeof(glm::vec3), &Vertices[0], GL_STATIC_DRAW);
 
	glEnableVertexAttribArray(0);
	glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (void*)0);
 
	glGenBuffers(1, &m_VBO[1]);
	glBindBuffer(GL_ARRAY_BUFFER, m_VBO[1]);
	glBufferData(GL_ARRAY_BUFFER, Textures.size() * sizeof(glm::vec2), &Textures[0], GL_STATIC_DRAW);
 
	glEnableVertexAttribArray(1);
	glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, (GLvoid*)0);
 
	glBindBuffer(GL_ARRAY_BUFFER, 0);
	glBindVertexArray(0);
 
	// This concludes the function 
}

Then, this is how I'm simply drawing the terrain to the scene:


void TerrainLoader::Draw()
{
	glBindVertexArray(m_VAO);
	glDrawArrays(GL_TRIANGLE_STRIP, 0, (m_HeightmapWidth - 1) * (m_HeightmapHeight - 1) * 4);
}

Alright, I hope that's enough information for someone to help me figure out the issue with this...

Thank you very much for taking the time to read my thread, I really appreciate it! Enjoy your day! 

Only had a very brief look, but wild guess, off by one error? i.e. to make a fence 10 metres long with fenceposts every 1 metre, you need 11 fenceposts, not 10...

Edit .. Ah no I think I see it ... you are using triangle strip, are you using an index buffer too? You need to reset the triangle strip after each row with a degenerate triangle or something like that.

Advertisement

If indexed draw, the glDrawElements call would be used to fire the render. I'm looking at the third parameter calculation in the glDrawArrays call as the bad guy, as a guess because it seems way too many for triangle strip.

 

Edit: looks like you're not building your triangle strip properly. Graph what you're doing on paper of what your double for loop does. When the loop starts, the first two triangles are correct. At the second iteration the first and second triangles are degenerate then the third and fourth fill the grid location hiding the problem. The error is repeated to the end. When a grid like this is built using triangle strip, a degenerate triangle is needed when you complete building a row and advance to the next row then repeat.

 

Dev careful. Pixel on board.
Buckle up. Everything will be revealed.

Thank you both for your reply.

On 9/29/2017 at 1:09 AM, lawnjelly said:

Edit .. Ah no I think I see it ... you are using triangle strip, are you using an index buffer too? You need to reset the triangle strip after each row with a degenerate triangle or something like that.

I'm not using an index buffer to draw my triangles, how do I reset the triangle strip after each row? 

Okay, problem fixed (FINALY!!!). Thank you so much for the help guys. Here's what I did for anyone interested, I stopped using triangle strips and I used indices instead with GL_TRIANGLES. That fixed the problem. I'm really glad I fixed this, for whoever encounters this problem and somehow stumbles upon this thread in the future, there were numerous solutions I researched to fixing this issue, I simply chose the indices/triangle approach as it seemed simple, but I will leave links for you here: 

https://gamedev.stackexchange.com/questions/143448/opentk-terrain-triangle-strip-issue (<-- This is basically my problem)

https://www.opengl.org/discussion_boards/archive/index.php/t-177729.html

The problem is really mentioned by the users above. I am very thankful for your help guys! 

15 hours ago, MaimaVanPersie said:

Okay, problem fixed (FINALY!!!). Thank you so much for the help guys. Here's what I did for anyone interested, I stopped using triangle strips and I used indices instead with GL_TRIANGLES. That fixed the problem. I'm really glad I fixed this, for whoever encounters this problem and somehow stumbles upon this thread in the future, there were numerous solutions I researched to fixing this issue, I simply chose the indices/triangle approach as it seemed simple, but I will leave links for you here: 

https://gamedev.stackexchange.com/questions/143448/opentk-terrain-triangle-strip-issue (<-- This is basically my problem)

https://www.opengl.org/discussion_boards/archive/index.php/t-177729.html

The problem is really mentioned by the users above. I am very thankful for your help guys! 

The indexed triangle list is indeed the right thing to use, but you need to generate a proper triangle index ordering or your GPU will have very poor cache utilization. There is many tool and algo to optimize this NP complete problem, one i like is this one : https://tomforsyth1000.github.io/papers/fast_vert_cache_opt.html

This topic is closed to new replies.

Advertisement