terrain rendering: adding roads

gjaegy

Newcomer
Hi guys,

I think I need your help, as I am a bit stuck...

I have implemented a terrain engine based on CDLOD, which is basically a chunk-LOD variant. Chunks are made of a 512x512 diffuse map (25cm or 50cm/texel), a height map and a normal map.

So far so good.

Now, I am trying to get roads being rendered on top of this terrain with a higher resolution, but I have already tried two options, each of them having severe drawbacks.

I would like to avoid any geometry-modification-base solution if possible...

The first option was requiring a pre-process step, where I was rendering the roads layer in a 4k x 4k texture for each chunk intersecting the road objects. This texture was then used in the pixel shader for a traditional multi-texturing approach. This gave pretty nice results on screen, however, the memory requirements was a bit of a problem (27 MB per 4k textures - DXT5 + mipmaps), as several chunks are kept in memory of course !

Then, I decided to try to render the roads dynamically, i.e. each time a chunk is being rendered, render the roads into a 4k x 4k render target, and use it while rendering the chunk (same multi-texturing approach). While memory usage has been reduced drastically (only 1 4k texture), this approach is slower (although completely un-optimized for now, so not a real problem really), but the main issue comes from the fact that no mip-maps are used, leading to horrible aliasing issue (see screenshot). I guess generating the mips maps dynamically is too expensive (remember there are more or less 20-30 chunks with such a road).

So basically, I would be happy if anybody had an idea, how I could solve this nicely...

Thanks guys :)

terrain.jpg
 
render to texture with clip map (megatexture) ? make sure the individual chunks are dynamically ressed down as per chunk geometry, and the amount of texture memory is bounded proportional to whats actually onscreen
 
keep in mind I know bugger all about rendering
the ground has a texture, paint a road on it
or
use a separate mesh for the road then align the terrain mesh to that road.
 
keep in mind I know bugger all about rendering
the ground has a texture, paint a road on it
or
use a separate mesh for the road then align the terrain mesh to that road.
Neither works that well in practice. A uniquely mapped terrain texture takes a lot of memory. Assuming you want for example 128x128 texels per square meter (a decent resolution for a first person game), and your terrain is 1000x1000 meters (a square kilometer) in size (a pretty small terrain by the way), you will need a texture of 128000 x 128000 texels to cover the entire terrain. That texture would take 16 gigabytes of memory (if we assume a single texel takes only one byte).

A separate road mesh works pretty well if the terrain does not need geometry LOD. Just carve the road out of the terrain mesh, and render the road separately. Of course you get no blending between the road and the terrain (sharp edges). In practice however you often want to render the terrain geometry with dynamic level of detail based on the distance to the camera. It's difficult to match the road geometry to the terrain geometry when the terrain geometry constantly changes during runtime. It's doable, but not easy to implement correctly (without graphics glitches and with as little overdraw as possible).

---

Virtual texturing (or Megatexture as id-software used to call it) is one way to solve this issue. It solves the decaling issue for more complex geometry as well, not just planar mapped terrain. During the rendering you only need to sample one texel from the virtual texture page cache (and one point sampled texel from the indirection texture) so it's very fast for terrain rendering, even if you need to blend dozens of materials on top of each other and/or have very complex decal shapes (like highly tessellated roads with lots of road markings). With virtual texturing you blend ("burn") the terrain materials and decals once to the page (usually 128x128 or 256x256 tiles) during page generation (when you handle your cache misses), not every frame again and again. A single 4096x4096 texture (= 1024 128x128 tiles) is enough to hold the page cache, and a single 1024x1024 texture is enough to hold the page mapping (indirection) data. So the technique is very memory efficient as well. If you want the best performance, and the ability to have lots of complex decals blended over the terrain, virtual texturing is a very good choice. If you only render terrain with virtual texturing, you do not need any "physical" virtual texture file, since you can generate the pages (tiles) based on the blending properties and decal information (virtual texture tiles can simply be generated on runtime by rendering alpha blended/textured 2d polygons by GPU).

The only downside of virtual texturing is that you need to somehow evaluate what pages are needed to render the scene. Usually this is done by rendering a lower resolution version of the view with a special shader that outputs the texture coordinates and mip levels of pages required. You need to process this information and update the page cache every frame. This processing of course takes some GPU and CPU time, but if you are targeting a platform with a multicore CPU, you can easily do the CPU side on a background thread. There are also several platform specifics you will have to take care of, if you want to implement efficient virtual texturing. The system is constantly updating GPU resources and requesting data back from GPU (GPU->CPU->GPU traffic), and doing this efficiently is the key for solid performance.

To get the most benefit from the virtual texturing, it's best to let it handle all your texturing needs (texture streaming for all meshes). However going fully virtual means you have to change your tools and content pipeline as well. I would say it took me over half a year to implement virtual texturing to our engine and content pipeline, so it's not a simple task. If you do not have a few extra months just to experiment with virtual texturing and/or you do not need the absolutely best performance, I would recommend the technique Humus introduced (link in last post). It's simple to implement and performs pretty well if you only have a few roads you need to blend over the existing terrain texture.
 
Last edited by a moderator:
Neither works that well in practice. A uniquely mapped terrain texture takes a lot of memory.

doesnt have to be unique, road looks pretty samey you can use tiles and multitexture
eg:


tile that, multtexture it and you end up with a half decent looking road (without the watermark obviously ;) )
 
doesnt have to be unique, road looks pretty samey you can use tiles and multitexture
Multitexturing and tiling itself is really simple, if you have one straight road. Just calculate texture coordinate to the road texture (transform 2d world coordinate though a 3x3 matrix to calculate the texture coordinate to road texture) and sample it (in pixel shader). If the texture coordinate (.x) is outside the [0,1] range you can skip the sampling and blending (but you still pay the cost to calculate the texture coordinate for every pixel, even if there's no roads in sight). So the scaling is pretty bad as you start to add more roads (and you can only have straight roads).

However if you want to add plenty of realistic curved roads (seen in the first post) the problem isn't blending and multitexturing. It becomes how to calculate the texture coordinate to the road decal from the world space coordinate you have in your pixel shader. The visible terrain area can have hundreds of curved roads, and calculating the intersection (and texture coordinate) to each in pixel shader is really slow. You have to partition the terrain in a way that only the road pieces that are intersecting the rendered pixel are considered. Additional material ID textures and material blending textures (for atlas lookups or texture array lookups) are often used to speed up the process (you can read from a lower resolution texture if there's a road at this location instead of looping though them all in the pixel shader). A blending system like this can become pretty complex (and slow) even for standard (tiled) terrain base textures that do not have any requirements for texture direction. Adding local directions to textures adds more complexity (and problem cases), so decals that are complex (curved roads) are often either rendered on top (separate draw call), are completely separate meshes or are rendered first to an offscreen texture and sampled later in the terrain geometry rendering pass. Virtual texturing is basically one way to implement the last option (and make it fully automatic), but there are lots of other options...

For example in Warhammer 40K Squad Command we rendered all terrain base materials and decals (including roads, and other direction dependant terrain textures) to a single offscreen texture that was projected on top of our terrain (simple 2d planar projection from above). It worked very well because the game was a strategy game, and the camera always looked downwards from the sky (you could rotate the camera and tilt slightly, but not that much). Additional bonus of this system was that we could render all shadows to this texture as planar projections (depth texture based shadow mapping basically wasn't a viable option for PSP).
 
hi guys,

thanks for all your inputs, I really appreciated.

I have actually completed this task, which I solved using a combination of two different techniques, creating two different layers.

One layer contains the base material (asphalt, concrete, etc...).
On top of that, a second layer is merged, containing markings and decals.

This approach allows me to have a great material detail (similar to detail mapping, but including color), without using a huge amount of memory, while allowing the flexibility of hand painted details, marking and other decals.

- for the bottom layer, a mask is generated as an offline process. I use a 32 bits integer texture to store the mask. Using 4 bits per material (to allow some transparency transition) I can store 8 masks per texture (one mask per material).
At runtime I blend the 8 materials together using the masks.

- for the top layer, I simply generate my texture tiles in the preprocess step, similar to what I do for the terrain texture (satellite image). I simply increase the resolution of the texture to allow higher detail.

below a small video showing the result (programmer art only !!), interesting part starts at about 0:25:

http://www.imagine3d.fr/_temp/terrain.wmv

Thanks once again for your help.
 
I use a 32 bits integer texture to store the mask. Using 4 bits per material (to allow some transparency transition) I can store 8 masks per texture (one mask per material).
At runtime I blend the 8 materials together using the masks.
That works well for simple materials. But if you have complex materials (color + normal map + per pixel properties = 3 textures at least per material), you would need to sample 8*3 = 24 textures in your pixel shader. That would perform acceptably only on highest end PC hardware. Mainstream hardware (and consoles) would need something more efficient.
 
Yes you are right. Hi-end PC is our target platform right now, so this is not a real problem currently.

Also, most of the time only a few (one or two) materials are used for a specific terrain tile (tiles are small), so I end up sampling only 3 or 6 (diffuse/normal/spec map) textures.

Any other optimization suggestion would be welcome :)
 
Back
Top