Tuesday, June 30, 2009

Indices Edges and Culling, Oh My!

Well its been a little while but I finally got some time to make a little more progress. One of the big issues that was coming up for me has been the edge details between LOD levels. While normally this is not that difficult an issue, when it comes to getting the most out of hardware instancing for the terrain we want to keep things as simple as possible.

The skirt method of covering LOD issues was looked at, but this to me seems very hacky. As well adding in many different indices to provide all the options for different edge combinations reduces the use of instancing because of the greatly increased draw calls and tendancy not to instance nearly as much.

So while I was pondering this I came across an article on the ROAM algorithm, while the algorithm its self really provides nothing useful when it comes to an instanced terrain, it does give us an interesting triangle layout that will greatly reduce the number of indices needed to have compatible edges. Typically with this triangle layout you only need a special grid every other level of detail, since 2/3 LOD edges will match by default. As well if you build your LOD structure to use square rings, you only need to place these blending grids against the connecting LOD and don't have to match the corner by simply increasing the LOD just a long the edge of the blending grid.

So with this in mind I re-built all my indexes to see how it would fare, and the results have been pleasant. Currently I am doing brute force LOD testing based on the range from the camera, this will become much more elegant and controled when I implement a culling method like Quadtree. The real trick will be that each grid needs to know if it is next to a higher LOD grid, and if so it will be added to the instance list for the correct LOD blending grids and rotated to match the edges up.

Here are a couple screen shots of the terrain as it stands now, this is 7 levels of LOD, and I suspect I will add another 2 to deal with the really distant stuff. as it is still fairly high detailed. You will notice I am still getting around 900 FPS with the brute force LOD and no culling on my 8800 GTX at 1024x768 resolution. This is with per pixel lighting and per pixel normal mapping based on a global normal map generated as needed at run time on the GPU. And to do all this I am only making 7 draw calls and letting the GPU do the rest with instancing.




Needless to say I am hoping to really get some screaming performance out of this system once everything is optimized and I implement quadtrees and fustrum culling.

Thursday, June 18, 2009

First Pass at LOD

So frame rates were not horrible with the instanced terrain method before, but I knew there was room for improvement, 210 FPS for a 1024x1024 terrain. Once I got the rough LOD system in using Index offsets and 4 Draw calls for the different LOD levels I about crapped my pants at the FPS improvement. The instanced grids degrade from 64x64 to 16x16 to 4x4 to 2x2, these are easily tweaked as well as the range.



My testing heightmap is total crap so nothing looks very smooth yet. And the expected gaps between LOD levels are there, but with the full 1024x1024 terrain in view and the camera centered so all LOD levels are engaged I am getting around 1360FPS. Thats a bit over a 600% improvement in frame rate.

The instancing method really starts to shine when I loaded in a 2048x2048 height map and clocked in an even 1200 FPS with the full terrain in view and LOD engaged.

Next step is to work on edges and normals.

Tuesday, June 16, 2009

The Terrain Less Traveled

There is no shortage of tutorials about doing terrain in XNA, including some great tutorials about GPU based heightmap terrain and various LOD methods that can be implemented. As I went through each of these with very little knowledge of C# and no knowledge of XNA I learned a lot, and fairly quickly felt much more comfortable with the framework.

But who wants to make something that everyone else has made, and while tutorials are great, if all of your components are simple carbon copies of various tutorials all mashed together I don't think you learn or grow. So in order to make things interesting I have started working on a GPU based Terrain using Hardware Instancing.

Initial Tests were promissing, right off the bat I was seeing a lot of improvement in frame rate over a simple static vertex buffer pre-generated during the load content process. Here you can see an early implementation of the instance based terrain, using 64x64 vertex grids instanced to match the heightmaps resolution per vertex. Early on I realised that I made a basic mistake in building my vertex buffer and only had 63x63 cells.

Fixing this and then making a couple small adjustments to how the texture coordinates were generated in the HLSL Vertex Shader things started looking much more promissing.

It should be noted that with the full instanced terrain, no LOD and no Culling I was seeing a solid 25-30 FPS performance boost over the static vertex buffer with and without the vertex buffer height sampling.

So now with some of the initial issues squashed I am moving on to implementing an initial crude LOD system. I plan on testing two approaches, the first being multiple draw calls with instance data for each LOD level and a few different grids stored in the vertex buffer, or alternatly would be swapping index buffers then drawing each LOD level. I think this will be a better choice since I can maintain my extremely small memory footprint with only a single 64x64 grid stored in memory. More pictures and results from that later.

It should be noted I would not have gotten nearly as far as I have without the brilliant mind of Jared Belkus making timely suggestions when I find myself in a corner.