Terrain + Quadtree Practices - UPDATE #1

Started by
11 comments, last by FoxHunter2 16 years, 8 months ago
Hi everybody I've worked myself though the Riemer's tutorials on terrain rendering, you can find them here: http://www.riemers.net/eng/Tutorials/XNA/Csharp/series4.php I refactored them a bit (put into diverse classes) and wrote my own Quadtree implementation based on some suggestions in this forums to do some culling. This is how it looks like (the shader is a bit incorrect right now) But I have some questions here: - Is it a good practice to store a VB and IB for each patch? - How can I remove the visible edges between each patch? I guess it's got something to do with the texture coordinates. If I use one large patch then the terrain looks fine, but I can't do any culling then, of course. regards [Edited by - FoxHunter2 on August 3, 2007 9:35:02 AM]
Advertisement
Quote:Is it a good practice to store a VB and IB for each patch?


I would appreciate an answer to this one myself as it's something I'm working on as well.

The approach I'm currently taking is to use a dynamic index buffer and a single vertex buffer. Traverse the quad tree and assemble the index buffer from the indices stored in the visible patches, then draw.
Original post by FunLogic
Quote:The approach I'm currently taking is to use a dynamic index buffer and a single vertex buffer. Traverse the quad tree and assemble the index buffer from the indices stored in the visible patches, then draw.


Does that mean you have a large vertexbuffer for the whole terrain and then create the indexbuffer on the fly for all visible patches? Do you read the visible vertices back from the VB or how do you do that?

I think this would be the only proper way to remove the edges in my terrain, but I don't know if it doesn't cost too much performance.
To save some memory on the graphics card (and maybe to speed things up a little bit too), I think it is a good idea to just use one index buffer for all the patches, since you draw each patch's triangles in the same order (or do you not?). Additionally you could store the entire terrain piece in one vertex buffer, just one patch after the other and jump into the right patch by offsetting the single index buffer mentioned above. That would result in no buffer changes at all.
Just a few ideas.
Quote:Original post by FoxHunter2

I think this would be the only proper way to remove the edges in my terrain, but I don't know if it doesn't cost too much performance.


The edges are the result of texture tileing. This is a common problem with multi-texturing.
So you basically mean just one VB and IB for the whole terrain and then just offsetting the IB? How would that work exactly, i.e. how would I choose the correct offset to only show the visible patches?
At first you would check, which patches of your terrain piece are actually visible (by whatever method you like, from easy as view frustrum culling to more elaborate with potential visibility sets (PSV)), by that you just gather a number of indices of which each identifies one of your patches. You then just multiply that number by the number of vertices per patch and this is your offset in the vertex buffer. Of course, the vertex buffer needs to be set up in the right way for that to work too (i.e. one patch after the other), but that's simple to do.

PS: Rereading my first post, I think I didn't make it completely clear what I meant with one index buffer for all the patches: This single index buffer would be holding only the indices to draw a single patch. It would then be reused to draw other patches as well.
That sounds really great - in theory. Since this is my first attempt in doing some terrain modelling I'm not sure I can do this without further reading.
It should be fairly easy to create a VB that will store all patches in a row. Actually my recursive quadtree division should do this by itself.
Something in the lines of

Subdivide(x, xx, y, yy, ...){   vertexBuffer[x + y...].Vertices = ...}


Is this correct?
But I yet have to see some code to get a grip on the whole indexing thing...
Okay, I managed to get all patches into one large VB and IB and from there just adjust the offset in my DrawIndexedPrimitive call.
Everything works fine and I got a ~200% performance boost, which is nice :)

But I still get the visible edges between each patch. Can I solve this by aligning my patches in a certain way in the VB or IB?
My new Terrain engine is divided up in such a way to try and resolve 2 problems.

1 - Not all of the terrain can fit in memory at the same time
2 - Not all of the in-memory terrain should be on screen at the same time.

To resolve the first problem, my terrain is broken up into TerrainTile's. These are fixed-size blocks of Terrain, which can be independently streamed to/from disk and contains the height, color, and UV information for the tile. Basically, each TerrainTile is a model. With my current implementation, the tile the player is currently standing on and the 8 surrounding tiles are always loaded in memory. When the user moves from one tile to the next, some are disposed of, while others are loaded.

Then, to deal with problem two, each TerrainTile is broken up into patches. These are dynamically sized sections of the terrain used for culling and optimizations. The sizes of these patches are based on user settings and performance. Each Patch has its own vertex buffer, index buffer, and segments which can be used for applying different effects, textures, etc...to different parts of the patch. Basically, each patch is a mesh.

So specifically, my new engine looks something like this:

Terrain   IndexList   TerrainTile[]       RootTransform       TerrainPatch[]           Transform           VertexBuffer           IndexBuffer           Effect[]           TerrainSegment[][]               baseVertex               startIndex


Whenever the player moves a call to Terrain.Update(). This updates the LoD, and also makes a call to Terrain.CullToViewFrustrum().

A Terrain.CullToViewFrustrum() call determines which of the tiles are visible, and then which of the patches are visible and marks them as such.

Rendering is a call to Terrain.Draw()

This iterates through each of the TerrainTiles and calls TerrainTile.Draw() on it. If the terrainTile is not visible, it simply returns.

If the TerrainTile is visible it sets the world matrix to the tile's root transform. After that it iterates through each of the patches and calls TerrainPatch.Draw on it.

This then multiplies its transform by the current world transform and sets that on the device. After that it sets the vertex buffer and index buffer on the device. Finally, it iterates through each of the terrain segment sets, rendering each segment by setting the desired material and rendering the triangles of that segment.

All in all, it seems to work ok.
Jeromy Walsh
Sr. Tools & Engine Programmer | Software Engineer
Microsoft Windows Phone Team
Chronicles of Elyria (An In-development MMORPG)
GameDevelopedia.com - Blog & Tutorials
GDNet Mentoring: XNA Workshop | C# Workshop | C++ Workshop
"The question is not how far, the question is do you possess the constitution, the depth of faith, to go as far as is needed?" - Il Duche, Boondock Saints

This topic is closed to new replies.

Advertisement