Michael's Blog

Procedural Generation! DevBlog #5

Published 16th April 20205 min readDevBlog
png
Image: Using noise to get random height value

I am finally back with my 5th DevBlog! Due to unforeseen circumstances of my computer breaking and COVID-19 it hindered my progress for a few weeks but now I am back on track with terrain generation fully working! So, this week the topic is heavily focused on the key aspects of terrain generation, multiple chunks working with terrain generation and seamless terrain between each chunk.

Terrain generation with perlin noise

As with games such as Minecraft, they used Perlin noise which is great for procedurally generating interesting terrain that is smoother and considers the previous and next random value giving it that more smooth terrain look. Compared to each value being random and having no correlation with the next or last value making it more volatile as you can see above. Started off there getting some basic noise in, also I found a great library called libnoise that creates a heightmap using Perlin noise and has good tutorials and is worth checking out!

image
Outcome: Using libnoise to generate terrain

I started getting somewhere with my second iteration of Perlin noise using libnoise. So basically, what I was trying to do was create a height map from Perlin noise and on the x, z axis (imagine looking at the height map from above) and going through each coordinate with each position having a value that you can access and getting a value between -1 to 1.

image
Outcome: Messing around with each height value

The third iteration is better than the last, so instead of half of the chunk has lots of voxels and is pretty height the other half doesn’t have any voxels at all which is not what I want. Compared to this iteration its more balanced with some areas that do still have voxels missing. I did figure out the reason why there were blocks missing, it was caused by values that were negative since the range is between -1 to 1 and I would multiply the value with the height of the chunk to get the proper height but if its negative it would give a negative value resulting in no blocking being drawn. In this iteration it got better with me multiplying the value by a certain amount then the height but still wasn’t what I wanted as it didn’t have that height and seemed very flat.

image
Outcome: Converting range from -1-1 to 0-1

Now the fourth iteration was so much better! It has a lot more height and seems more like terrain. What I did to fix the issue of holes in the terrain was convert the range to 0 to 1 (which is what its supposed to be) then multiply by the chunk height and have some checks in case because values can sometimes still be slightly below zero or past the height. I then tried to add multiple chunks but no matter what each chunk just didn’t seem to add up….

						
for (int x = 0; x < ChunkSize; x++)
{
	for (int z = 0; z < ChunkSize; z++)
	{
		float Height = (heightMap.GetValue(x, z) + 1.0f) * 0.5f; //Get the height map x, z coordinates then convert range from -1-1 to 0-1
		Height = (Height * (ChunkSize - 1) * 1.0f) * 1.0f;

		if (Height > 31.0f)
			Height = 31.0f;

		if (Height < 0.0f)
			Height = 0.1f;
		
		for (int y = 0; y < Height; y++)
		{
			m_Blocks[x][y][z].SetActive(true);
		}
	}
}
						
					
image
Outcome: Made height map correct size to relative chunk

Leading to my final iteration of terrain generation, I eventually figured out that with libnoise a size of the heightmap needed to declared which I did but was 256 x 256 meaning the height map was way bigger than the chunk itself so the chunk only used part of that height map meaning it would never line up perfectly with other chunks and once I set the size to 32 x 32 (32 is the size of a chunk) it was how I wanted it and even looked better with more hills and ground.

Multiple chunk terrain

During having multiple chunks, I talked about the issue I had above of it not lining up correctly which was a headache. I decided I needed to have a chunk manager that I have one of and have a 2D array of chunks to render. Once I done all that I decided the best way render chunks in different positions would be to have an offset of each chunk as a uniform vec3 and send it to the shader each time a chunk is rendered as seen below. I looped through each chunk and using x,z to offset each chunk and multiplying the chunk size so its offset far enough.

						
for (int x = 0; x < m_AmountOfChunks; x++)
{
	for (int z = 0; z < m_AmountOfChunks; z++)
	{
		shader.Bind();
		shader.SetUniform3f("u_offset", x * m_chunks[x][z].ChunkSize, 0, z * m_chunks[x][z].ChunkSize);
		m_chunks[x][z].Render(BatchVertexArray[z + x * m_AmountOfChunks], shader);
	}
}
						
					
image
Outcome: Offseting each chunk accordingly

Seamless Terrain Generation

After getting multiple chunks working with offset, I still needed to make it seamless as the image above shows the same terrain for each chunk making the terrain look weird. Luckily libnoise has a parameter when making a heightmap that you must set the bounds of the map and so for each chunk I passed in the x,z variables so the bounds were moved for each chunk to make it all seamless. For setting the bounds you needed a lower and upper for x and z, so I set the upper bounds for x and z to + 1 and set the lower x and z to just x and z, that’s it! Now a seamless terrain!

						
heightMapBuilder.SetSourceModule(PerlinModule);
heightMapBuilder.SetDestNoiseMap(heightMap);
heightMapBuilder.SetDestSize(ChunkSize, ChunkSize);
upperX = x + 1.0;
upperZ = z + 1.0;
lowerX = x;
lowerZ = z;
heightMapBuilder.SetBounds(lowerX, upperX, lowerZ, upperZ);
heightMapBuilder.Build();
						
					
gif
Gif: Seamless terrain with wireframe

Overview

My progress with terrain generation has went pretty good with some setbacks I expected but wasn’t too difficult to get it in. Libnoise is a very good library for making seamless terrain. I feel like I am on the final stretch to achieving the rest of my goals such as:

Goals
  • Loading and unloading chunks as the player moves
  • Asynchronously load chunks each frame
  • Optimisation - frustum culling, culling unseen voxels between chunks, render distance
  • ImGui(Graphical interface) adding test framework such as changing the seed, allowing the user to customise terrain generation and showcasing features I've implemented with toggles