Terrain, Part 7

By Shamus
on Feb 13, 2006
Filed under:
Programming
A Failed experiment

So far the terrain has been colored using a half-baked scheme I cooked up at the beginning. This system was intended to be temporary, until I can use proper texture mapping.

If you recall, here is how things look now:



The surface looks bland because it doesn’t have a texture. You can think of it as the grassy areas being made of this:

Intead of this:

The latter is far better because it will look more interesting. But… in order to use textures, you need to blend them together. Some polygons will be grass (as above) and others will be dirt:

And it won’t look right to have a hard line between them:

Instead, you want to “transition” from one surface to another, sort of like this:

My first solution was a failure. My thought was to render the entire terrain first as grass, then render another layer over top of it using the “dirt” texture. (Polygons that didn’t have any dirt would be left out.) Imagine you’re painting a wall. The left side of the wall needs to be red, and the right blue. You paint the wall solid red, and then paint over it again in blue paint, laying the paint on thin on the left and making it thicker as you go right. When you’re done you’ll have a nice smooth gradient from red to blue. Obviously you’re painting the wall twice, and the second coat is a lot more difficult than the first.

I implemented this, and it more than doubled the number of polygons the program had to draw. Worse, the extra transparent layers were much slower than drawing opaque polygons. And finally, if you remember back in Part 3 of this series that my program cuts up the terrain into triangles. The closer you are, the more triangles you get. Well, the changing shape of the polygons underneath didn’t look so bad using flat color, but when using fading textures as I just described it looks terrible. The patterns of textures fading into each other would shift whenever the viewpoint moved around, which was very distracting.

So the system looked worse, was slower, and was also a lot more complex. After investing some time into this, I concluded the entire idea was flawed and unworkable. Time to start over.

Zone Textures

After giving the problem a lot of thought, I’ve come up with a different way to handle things. First, I break the terrain into seperate areas. (This was done back in part 4, to solve the stuttering problems I was having.) I call these parts “zones”. Here, I’ve rendered the scene with each zone a different color:

For each zone, my program will create a special texture just for that zone. It puts all of the transitions and fading into this texture. Going back to the wall-painting analogy, this is like making a big piece of wallpaper with the red-blue fade built in. The wallpaper will ONLY work on this wall, BUT it can be slapped in place just as quickly as a single coat of paint.

I can spend as much time on this texture as I want. Intead of simplifying the pattern, I just draw all of the fading for every tiny little polygon in the zone. After all, wallpaper takes the same length of time to put up no matter how simple or complex the pattern is.

Here is an example of one of the textures it makes:

It will then slap this over the intended zone. The results are stunning:

I’m back to rendering each polygon only once. This system is simple. It doesn’t hurt framerate. It does have a few disadvantages, though:

  1. It takes a couple of seconds to create the textures at startup. If this were a game, this would contribute about 2 more seconds to the “loading screen”.
  2. These textures use quite a bit of memory. I’m using 64 of them, and at the resolution I’m using now they will eat up about 16MB of video memory. That is acceptable by today’s standards, although I do need to keep an eye on this.
  3. The ground looks blocky in close-up. Check it out:

    Again, this is not a deal-breaker, since one of the original goals of this project was to make terrain that looks good from above and not close up. Still, I’d like to have done better than this. I can make it look better by using higher resolution textures, but that will up the video memory useage from 16MB to 64MB. This is still acceptable, but I want to see if I can come up with any more bright ideas before I use the cudgel of more CPU and memory. I’ll have to give this more thought.

I’m happy with how this turned out. I know I said I wasn’t going to prop up the engine with a lot of makeup, but I can’t resist adding one shot with fog enabled:

This is starting to look pretty nice.

Enjoyed this post? Please share!


5Five easy comments.

From the Archives:

  1. Zenja says:

    Its interesting to read about the various terrain rendering techniques out on the WWW. One thing that I found very puzzling was why are there tons of articles about using various techniques like ROAM, Geomorph mapping etc with heaps of math and optimisation techniques written from 1994-2001, and then not a single article since late 2001. Well, the answer to this riddle can be attributed to Moores law.

    After my basic first pass attempt at creating a terrain engine, I did some research and tried using various techniques to render terrain, and even tried to use some 3rd party engines (Demeter and the Ogre3D terrain engine), and only got around 200 fps for rendering a plain terrain with 50,000 triangles or so. These terrain engines use various techniques outlined in various documents on the web, and none of them handle mixing textures well.

    Using a brute force approach with plain Vertex Arrays, I get >1500 fps with a single texture. As far as the rendering pipeline is concerned, passing a single pointer to preformatted data (GL_T2F_C4F_N3F_V3F) which fetches data across modern AGP / PCI express buses is much much faster than worrying about CPU bound terrain optimisation techniques. Once modern graphics silicon arrived, no one bothered with complex CPU based optimisations since it accelerates the wrong bottleneck. Thats why there are no NEW articles about terrain engines, and using plain old Vertex Arrays (or Vertex Buffer Objects) is faster than older rendering techniques.

    Likewise, mixing textures introduces another new obstacle to the terrain mix – and the modern consensus seems to be to use pixel shaders for blending of textures. This is definately the path future hardware will evolve towards.

    A side project I’m working on (with terrain rendering) uses a slighly different technique for mixing textures. My aim is to minimise pipeline stalls, so I remove anything which forces the GPU to wait for instructions from the CPU, which sadly also includes sacrificing Pixel Shaders. I can never stop being amazed at how efficient plain old Vertex Arrays are, so I arrange my arrays to match a texture – and my texture has a transition from grass to dirt. I divide the texture into 3 sections, where the first section is pure grass, the second is the transition and the 3rd section is dirt. As we draw a triangle strip, the texture coordinate floats between 0 to 0.33333 and back again. There is a 50% chance that we may be at 0 when we need to start the transition, so that means that we will be late by two triangles (quad) which we draw with the wrong texture. This creates a cute effect of grass overflowing onto the dirt every now and then, but is acceptable. These can be corrected later on the 2nd pass when we add more detail to the terrain (blades of grass, roads etc).

    So in effect, the final rendering routine is incredibly simple – bind a texture, call DrawArray(pointer1, count1), bind a 2nd texture, call DrawArray(pointer2, count2), and so on. Calculate pointer and count when the view changes. Nice and simple. And ridiculously fast. The best thing about this design is that there are no pipeline stalls during rendering. And this is why I get 5x the speed using brute force (and overdrawing quite a large number more triangles than necessary) compared to using a CPU bound complex algorithm which draws less triangles, but encounters stalls along the way.

    A slow truck on a highway bypass can travel faster than a sports car in city gridlock, even though the city route has distance. Stalls are a killer for modern graphics.

  2. Shamus says:

    There is a lot to think about here. Many of the issues you mention were things that hadn’t occoured to me yet.

    I hadn’t considered that the agonizing over poly counts might be a waste of time. I’ll need to give this a lot more thought.

    I tried pixel shaders in the past, on other projects. Keep in mind that I’m using some fairly old hardware here (1ghz system and a GeForce 5200), but I found rendering polygons that need 4 texture lookups (which is the worst-case for a terrain triangle, a texture for each corner plus one for lighting) is very hard on framerate. At the time I was using pixel shaders to render a sky with moving cloud layers, and it was actually faster to render it in 3 passes than to make a custom shader that did 3 lookups. I filed this under “stuff to investigate later” and moved on.

    I can’t picture the trick you’re using for the grass transition. I’m sure you’re doing something clever, but I can’t get my head around it.

    • WJS says:

      If you were to do this again, ten years down the line, would you use pixel shaders if you were doing it today? They were the first thing that sprang to mind when you mentioned how textures needed blending.

  3. Joe says:

    I always wonders how the images were rendered. I play a ton of games and the textured polygons and final renderings do not look anywhere near as good as what you have done.

  4. WJS says:

    Wow, it’s amazing to recall that at one point we considered 16MB significant. Right now, I’m using about 20 times that just sitting on the desktop with a handful of basic apps open. The browser would be the only one I would expect to significantly load the graphics.

Leave a Reply

Comments are moderated and may not be posted immediately. Required fields are marked *

*
*

Thanks for joining the discussion. Be nice, don't post angry, and enjoy yourself. This is supposed to be fun.

You can enclose spoilers in <strike> tags like so:
<strike>Darth Vader is Luke's father!</strike>

You can make things italics like this:
Can you imagine having Darth Vader as your <i>father</i>?

You can make things bold like this:
I'm <b>very</b> glad Darth Vader isn't my father.

You can make links like this:
I'm reading about <a href="http://en.wikipedia.org/wiki/Darth_Vader">Darth Vader</a> on Wikipedia!

You can quote someone like this:
Darth Vader said <blockquote>Luke, I am your father.</blockquote>