11
\$\begingroup\$

I'm creating an XNA game that requires a huge space for players. Currently, the test heightmap I'm using is 4096x4096, and is saved as a 4-bit BMP.

What I'm trying to do is take that huge heightmap file and render it in the game. The problem I'm running into is the fact that it's inefficient to load the entire terrain into memory at once, as it will use majority of available memory.

Another problem I've run into is that I cannot render the terrain all in one primitive because of a hard-limit set within XNA.

With that said, I've come across a number of solutions, all of which I have listed below:

  • Rendering based on the current user's location - basically drawing a square around the user no matter their orientation within the world. This isn't exactly what I wanted either, because you're still rendering space that the user does not see.
  • Rendering based on the user's orientation and position - I found a formula to retrieve a triangle that is supposed to have what pixels of the heightmap are supposed to be rendered, but this proved to be very difficult.
  • Splitting the terrain into multiple chunks and rendering which ones are closest to the user - Still not very efficient as you're still rendering chunks that people won't see. And it's work-intensive because then I have to split my heightmap into several pieces, and scalability becomes a large problem.

After trying those solutions, I'm fresh out of ideas for what to do. I've received some answers where people are telling me to do these complex algorithms, but I simply have no idea even how to approach doing them.

So basically I'm asking for a simple, straightforward way of rendering humongous terrains in XNA with utmost efficiency.

I'm rather new to game development in general, but I am willing to research if it seems promising.

Update 1: After researching the geoclipmapping method, I started to code with that. I have all the math done, and the game runs. However, it is extremely inefficient - which is probably bad coding on my part. It runs at 2FPS and uses an entire core of my CPU. I'm going to try and improve the code, but I think I'll need more help, so here is a Pastebin of the code for the Terrain manager class. I'll post back with more results later if I ever get it to be more efficient.

\$\endgroup\$
1
  • 1
    \$\begingroup\$ Interestingly, the technique you're talking about seems similar to the one that ID Software are using in their upcoming game, Rage. They use a 'megatexture' and then stream the parts of it that are required into the GPU. He's done a talk about it, but heres a wikipedia article, it might be inspirational: en.wikipedia.org/wiki/MegaTexture \$\endgroup\$
    – Russ C
    Aug 19, 2011 at 16:44

3 Answers 3

5
\$\begingroup\$

The chunks approach is typically what is used. Rarely is it efficient to test every triangle out of hundreds of thousands to see if you should be rendering it. Instead most terrain rendering algorithms employ a spatial data structure to dynamically render the visible parts of the terrain.

An easy to implement data structure is called a quadtree. In short, to use a quadtree you would find the player's viewing frustum, intersect it against the top level of the quadtree, and for any chunks that are partially viewable (ie, the frustum planes intersect the chunk) you subdivide and test all the children chunks, omitting ones outside the frustum. This will give a pretty close approximation to the actual visible geometry with only a few levels of recursion.

More advanced terrain renderers use an algorithm to tune not only the viewable geometry but the detail of that geometry as well. Geomipmapping (and its relative geoclipmapping) is relatively popular at the moment for doing that but isn't a trivial thing to implement.

edit: Here is a decent description of both geoclipmapping and the frustum culling.

I also have some doubt as to whether 4 bits for the heightmap is actually enough to produce a nice looking terrain unless you're doing a lot of smoothing on the result.

\$\endgroup\$
1
  • \$\begingroup\$ I've taken a look at that article, and I've decided that I'm going to go with the geoclipmapping method first, as it looks like it'd be the most efficient at displaying a large amount of terrain. I'll post back with my results. \$\endgroup\$
    – sammarks
    Aug 19, 2011 at 20:55
3
\$\begingroup\$

Any approach that requires you to do per-frame work to load data onto the GPU is going to be flawed.

Here's a rough outline of one approach that should perform well:

You should be dividing your terrain up into (fairly large) chunks, loading those chunks into fixed vertex buffers (the bit-depth of your height-map doesn't matter!). Those vertex buffers will simply sit in GPU memory, waiting to be rendered. You'll have to experiment with what is an appropriate chunk size, but 128x128 is perhaps a good place to start.

For a 4096x4096 terrain you're a little bit beyond the limit of what I'd be comfortable loading onto the GPU at once - it's probably a few hundred MB of vertex data (although you could get it down to ~64MB if you're clever). So you may have to load and unload vertex buffers from the GPU in the background.

(If you implement background loading of chunks, this should be extremely scalable!)

After you have the vertex data on the GPU it is an appropriate time to do visibility culling on a per-chunk basis. There is no need to send the command to render a chunk, if you know it's behind the camera.

You should almost never be doing per-triangle culling on the CPU!

The GPU will cull triangles that are off-screen much faster than you'll ever be able to.

For more information about performance, take a look at this answer over on the Game Dev site.

\$\endgroup\$
0
\$\begingroup\$

I'm not an expert at XNA by any means so please correct me if I am wrong but I was under the impression that there is actually built in optimization for situations like this. I know you are able to set a render distance and after that point it does not render anything, in your case it would be the remaining terrain. However this leaves a fairly unattractive edge to your rendered world, so you would have to implement something like fogging that most open world games have.

\$\endgroup\$
1
  • \$\begingroup\$ There are built in solutions, but the problem I ran into was that I was trying to render one large primitive, which exceeded the limit on polygons for primitives. Therefore, it wouldn't draw, just throw an exception. \$\endgroup\$
    – sammarks
    Aug 20, 2011 at 15:36

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .