Terrain LOD

Started by
7 comments, last by RobM 8 years, 7 months ago
Hello everyone.
I'm an currently working on the design of a terrain system for my engine. The goals are to be able to render large terrains with good quality in real time. I'm mostly working on the geometry pipeline so far. The literature is quite abundant but i mostly retained the ideas from the following papers :
So far the geometry pipeline will looks like this :
- Terrain will be rendered using a heightfield (texture).
- The terrain grid will be a minimal vertex buffer containing only the x and z coordinates.
- The displacement will be done in the vertext shader.
- Vertex normals will also be computed in the vertex shader.
- The redering will be done using a quadtree that will be used for both culling and lod selection. The quadtree is basically only managing index buffers.
In order to avoid T-Junctions between adjacent nodes with different lod, a certain number of index buffers permutations will be used (9 of them) just like it is described in [1].
The problem is that for this algorithm to work, the difference in lod level between two geometrically adjacent nodes on the terrain must must be at most 1.
What would be a good way to impose this restriction ? Right now, i'm out od ideas or at least good ones ...
We think in generalities, but we live in details.
- Alfred North Whitehead
Advertisement

Yeah... some of these ideas are not very good. You only load the terrain once, and that's at level initialization time. If you're pulling the height information from a height map, there's no reason why you can't just set the exact world coordinates for each terrain vertex. If you do this for every frame inside of the vertex shader, your GPU is going to be doing a lot of unnecessary processing work which could just be avoided by placing the XYZ coordinates at load time. Multiply that by every vertex in your terrain, and its going to add up.

The same applies to vertex normals. There's no reason why you can't just get this info at load time and store it in a custom vertex object for each terrain vertex. In fact, if you're going to be creating a height map texture for your terrain, you can also create a normal map texture for your terrain.

You're making 3D terrain. You don't use quad trees for 3D environments, you use octrees.

To avoid the T-junctions, you want to use skirts. The LOD level of a terrain chunk should NOT be influenced by the LOD level of an adjacent terrain chunk. In my terrain system, I had four LOD levels, and I had to be able to skirt lower LOD's to higher LOD's. Check out this wireframe to see what I mean:

[attachment=28739:TerrainLOD.png]

Yeah... some of these ideas are not very good. You only load the terrain once, and that's at level initialization time.

The terrain can be very large and will not necessarily be entirely loaded at once. With a structure like this, the index buffers of the leaves can be reused because chunks are loaded in leaves.

The same applies to vertex normals. There's no reason why you can't just get this info at load time and store it in a custom vertex object for each terrain vertex. In fact, if you're going to be creating a height map texture for your terrain, you can also create a normal map texture for your terrain.

With modern hardware, which is the target platform, it is faster to do a bit more calculations then to transfer larger amount of data. This is especially true when loading out of core data in the background.

You're making 3D terrain. You don't use quad trees for 3D environments, you use octrees.

The quadtree is the data structure used for culling and lod selection. Each node will contain the maximum and minimum height of the terrain for the area covered by the node.

We think in generalities, but we live in details.
- Alfred North Whitehead

If you're using DirectX 11-grade graphics cards, doing the LOD tessellation in the Hull/Domain shader stages is fairly neat, and avoids some of the difficulties of having to patch up T-junctions between LOD levels. I've played around with it both this way, and more the way you are describing:

Not the greatest screenshot, but this is from my hardware-tessellated version. I'm sure that I'm not doing things in the most optimal way, but on the hardware I was using(GTX560), the hardware-tessellated version was considerably faster.

image_thumb%25255B11%25255D.png?imgmax=8

Eric Richards

SlimDX tutorials - http://www.richardssoftware.net/

Twitter - @EricRichards22

If you're using DirectX 11-grade graphics cards, doing the LOD tessellation in the Hull/Domain shader stages is fairly neat, and avoids some of the difficulties of having to patch up T-junctions between LOD levels. I've played around with it both this way, and more the way you are describing:

Not the greatest screenshot, but this is from my hardware-tessellated version. I'm sure that I'm not doing things in the most optimal way, but on the hardware I was using(GTX560), the hardware-tessellated version was considerably faster.

Yes, hardware tessellation is something i need to investigate more. I have toyed a bit with a terrain tessellation demo that was published in GPU Pro 4 and it looked very neat indeed. The performace were very good on my development machine (GTX 780 Ti x2). When i tried it on a smaller card (Quadro 600) which has DX11 feature level but much less cores, it was running fine but very close to the minimum frame time with only the terrain.

I'm thinking tessellation might be good for smaller tasks like morphing in character animation when the character is close to the camera.

Tessellation is indeed interesting for very dynamic surfaces like character's faces and water simulation. Like i told you, i need to get a good grip of it and the associated cost.

I may also have to support DX10 level hardware so i will need a falback.

Tank you for your post, i will read your references when i get back home.

We think in generalities, but we live in details.
- Alfred North Whitehead
I do a similar style terrain to what you're proposing. I stitch terrain patches with different lods together in the vertex shader by simply moving the edge-in-question vertices of the higher LOD patch to match those of the lower LOD patch. Performance is great.

I have made one or two posts on the subject on this forum a few years ago, I'll see if I can dig them out

What would be a good way to impose this restriction ?

Culling is done via the quad-tree.
With proper memory management, you can jump directly to adjacent cells, as well as up and down LOD levels.
When beginning the search through the quad-tree, perform a standard parent-child iteration over the tree, but select child nodes in order of closest-to-camera first.
This allows you to find the chunk closest to the camera and its appropriate LOD level.

From here, change your search method so that it branches out and away from that chunk, and for each node you pass a minimum and maximum search depth. For each neighboring node, you may only go up or down one level in the tree.

As for proper memory management, this method becomes possible if you lay out all nodes sequentially in RAM and hold an array of pointers to the start of each level. To go from Node [X, Y] to its parent, simply go up one level (use the array of pointers to the starts of each level) and index into that array of nodes [X>>1, Y>>1]. Nodes on a level will be organized like a bitmap, so you can also easily go left, right, up, and down from any given node on the same level, allowing to traverse from any node directly to any other node.

You would organize your quad-tree this way for best cache usage anyway.


L. Spiro

I restore Nintendo 64 video-game OST’s into HD! https://www.youtube.com/channel/UCCtX_wedtZ5BoyQBXEhnVZw/playlists?view=1&sort=lad&flow=grid

I have made one or two posts on the subject on this forum a few years ago, I'll see if I can dig them out

Thank you, i am very interested by the approach.

Culling is done via the quad-tree.

With proper memory management, you can jump directly to adjacent cells, as well as up and down LOD levels.
When beginning the search through the quad-tree, perform a standard parent-child iteration over the tree, but select child nodes in order of closest-to-camera first.
This allows you to find the chunk closest to the camera and its appropriate LOD level.

From here, change your search method so that it branches out and away from that chunk, and for each node you pass a minimum and maximum search depth. For each neighboring node, you may only go up or down one level in the tree.

As for proper memory management, this method becomes possible if you lay out all nodes sequentially in RAM and hold an array of pointers to the start of each level. To go from Node [X, Y] to its parent, simply go up one level (use the array of pointers to the starts of each level) and index into that array of nodes [X>>1, Y>>1]. Nodes on a level will be organized like a bitmap, so you can also easily go left, right, up, and down from any given node on the same level, allowing to traverse from any node directly to any other node.

You would organize your quad-tree this way for best cache usage anyway.


L. Spiro

Thank you very much for the detailed explanation.

We think in generalities, but we live in details.
- Alfred North Whitehead


Thank you, i am very interested by the approach.

Here you go:

http://www.gamedev.net/topic/532295-terrain-lod-stitching-on-the-gpu/?hl=stitching+terrain#entry4442187

Happy to explain any of it if you get stuck, although it's been a while...

This topic is closed to new replies.

Advertisement