Procedural Terrain Optimizing
by Bill Vee · 10/11/2012 (10:53 am) · 4 comments
I have been optimizing the terrain code in an effort to decrease loading times for each terrain chunk. There turns out to be 2 bottlenecks in the process.
This is what happens when I create a new chunk of terrain data.
1. The calculations done for a given x y z point to determine if it is solid or empty, it's density actually, use noise functions that are very complex and require a lot of calculating time , as much as 3,400 calculations per point.
2. Once the densities are done then the actual geometry can be calculated. This too takes a lot of cpu cycles per chunk.
My first attempt put both the density and geometry calculations on a separate thread to be processed. While this works and decreases the time it takes to load each chunk ,it is still not as fast as I would like.
Part of the problem with this approach is that I can't create the vertex and index buffers outside the main thread. But I can have the vertex and index data ready to be processed when the thread is done with a chunk and returns this data back to the main thread. But the time it takes to create the buffers on the main thread is not that bad per chunk. The problem occurs when chunks start being returned faster than can be converted into vertex/index buffers.
At this point I have 2 options.
1. Process each chunk as it is returned, which will effect frames per second.
or
2. Put the returned chunks in a "list" to be processed at a steady rate that would minimize impact on the frames per second but would be noticeable in seeing terrain chunks popping up in between frames.
It may turn out I will simply have to live with the "popping" effect.
This is what happens when I create a new chunk of terrain data.
1. The calculations done for a given x y z point to determine if it is solid or empty, it's density actually, use noise functions that are very complex and require a lot of calculating time , as much as 3,400 calculations per point.
2. Once the densities are done then the actual geometry can be calculated. This too takes a lot of cpu cycles per chunk.
My first attempt put both the density and geometry calculations on a separate thread to be processed. While this works and decreases the time it takes to load each chunk ,it is still not as fast as I would like.
Part of the problem with this approach is that I can't create the vertex and index buffers outside the main thread. But I can have the vertex and index data ready to be processed when the thread is done with a chunk and returns this data back to the main thread. But the time it takes to create the buffers on the main thread is not that bad per chunk. The problem occurs when chunks start being returned faster than can be converted into vertex/index buffers.
At this point I have 2 options.
1. Process each chunk as it is returned, which will effect frames per second.
or
2. Put the returned chunks in a "list" to be processed at a steady rate that would minimize impact on the frames per second but would be noticeable in seeing terrain chunks popping up in between frames.
It may turn out I will simply have to live with the "popping" effect.
About the author
#2
Because you could potentially take your initial seed values, and do a lower resolution pass first and run it in a wide area to create a 'low LOD' version for distance viewing, and generate this in larger chunks.
Then as they get closer, you go back through and generate the "proper" version with the full detailing, correct calculations, etc, and fade it in as Ron suggests to replace it.
Think like how the elder scrolls games handled it, a pretty low res mesh for the distance views, and the high res mesh that blends/pops in as the player traverses around.
It doesn't fix it entirely, but depending on the low-res chunk size you could do at a time, you may be able to generate the low-res mesh far enough out that you could hide the generation steps with distance fog or the like.
Of course, all that is on the presumption that you can do a 'lower res' pass on the heightmapping generation to make that low res distance mesh. I'm not sure if the way you're doing it allows for that option.
10/11/2012 (1:13 pm)
The other possibility that comes to mind to me, is I'm wondering how far back you can scale the detailing/resolution of the density/heightmapping code.Because you could potentially take your initial seed values, and do a lower resolution pass first and run it in a wide area to create a 'low LOD' version for distance viewing, and generate this in larger chunks.
Then as they get closer, you go back through and generate the "proper" version with the full detailing, correct calculations, etc, and fade it in as Ron suggests to replace it.
Think like how the elder scrolls games handled it, a pretty low res mesh for the distance views, and the high res mesh that blends/pops in as the player traverses around.
It doesn't fix it entirely, but depending on the low-res chunk size you could do at a time, you may be able to generate the low-res mesh far enough out that you could hide the generation steps with distance fog or the like.
Of course, all that is on the presumption that you can do a 'lower res' pass on the heightmapping generation to make that low res distance mesh. I'm not sure if the way you're doing it allows for that option.
#3
@ Jeff - I can sample the data at any resolution I want. But the way I currently create the geometry is to create the hires data first then decimate that data to produce a lower res chunk that is displayed at a certain distance away from the camera. You can see this in the videos I posted already if you look at the terrain about 50 meters or so from the camera, as I move the shading on it kinda pops to a slightly different texture. That is where the hires and lores geometries are being swapped.
Here are some pics to show the hi an low detail.


But getting back to your point about a lowres pass on the data , while I have mostly shown a heightmap type of terrain keep in mind that the shape of the terrain is completely arbitrary. It can have caves and overhangs and "floating" islands.
Because I am using marching cubes there are some problems sampling data at different resolutions and connecting them side by side.
Google "transvoxel algorithm" and you will see what I mean.
My recent work on optimizing has made me a lot happier with the results and coupled with Ron's fade idea I think I will be able to live with it for now.
10/12/2012 (1:53 pm)
@ Ron - I like the fade in idea.@ Jeff - I can sample the data at any resolution I want. But the way I currently create the geometry is to create the hires data first then decimate that data to produce a lower res chunk that is displayed at a certain distance away from the camera. You can see this in the videos I posted already if you look at the terrain about 50 meters or so from the camera, as I move the shading on it kinda pops to a slightly different texture. That is where the hires and lores geometries are being swapped.
Here are some pics to show the hi an low detail.


But getting back to your point about a lowres pass on the data , while I have mostly shown a heightmap type of terrain keep in mind that the shape of the terrain is completely arbitrary. It can have caves and overhangs and "floating" islands.
Because I am using marching cubes there are some problems sampling data at different resolutions and connecting them side by side.
Google "transvoxel algorithm" and you will see what I mean.
My recent work on optimizing has made me a lot happier with the results and coupled with Ron's fade idea I think I will be able to live with it for now.
#4
And I'll admit, I completely forgot about overhangs and caves when I was talking about that part. Though for caves, you could cheat somewhat due to limited visibility distances, you wouldn't need to do the wide low-res pass.
But overhangs would definitely cause problems, you're right.
Even with minor pop, since it saves to disk(presumably you can save it and recall it on later playthroughs as well) the terrain quality it outputs is still amazing. You've got limitations for live-streaming, but even minecraft gets load popping, and I don't think you've got nearly as much going on geometry-wise there as what you're doing here.
For more 'conventional' usage, the quality it puts out seems to trump any minor issues when generating it all on the fly the first time around.
10/12/2012 (2:45 pm)
Ah, so that's what that shading pop was, that makes sense.And I'll admit, I completely forgot about overhangs and caves when I was talking about that part. Though for caves, you could cheat somewhat due to limited visibility distances, you wouldn't need to do the wide low-res pass.
But overhangs would definitely cause problems, you're right.
Even with minor pop, since it saves to disk(presumably you can save it and recall it on later playthroughs as well) the terrain quality it outputs is still amazing. You've got limitations for live-streaming, but even minecraft gets load popping, and I don't think you've got nearly as much going on geometry-wise there as what you're doing here.
For more 'conventional' usage, the quality it puts out seems to trump any minor issues when generating it all on the fly the first time around.

Associate Ron Kapaun
3tdstudios.com
Been following this for a little while now and I find it very interesting. As for the popping effect, (which I think makes the most sense from a FPS standpoint) I think it would be relatively simple to add a 'fade-in' shader into the process and it may reduce the 'popping' enough to make it a bit more tolerable. I know there is a chunk of code associated with the ground cover that tries to 'fade in' the outer edges of the ground cover objects. Not perfect but might be a place to start.
Just a thought.
Ron