The Question Of Range
As I did most of my backlog yesterday, I have spent most of the afternoon contemplating the meaning, benefits and challenges of LOD. Before I set to work creating code, experience tells me that it's vital I have a very clear picture in my mind how it should work and all the intricate dependencies that will be hung from it.
I spent some of Monday and most of today closing my eyes and thinking about exactly how it must work to solve all the issues thrown up. One issue is what the performance will be when you are constantly adding polygons to the LOD levels (both high and low LOD buffers), what the popping will look like if you place the range at 10 tiles, what the view would be from upon high when editing the level from a high vantage point in the map editor, what the trade-offs are between memory consumption, performance of writing polygons on the fly and the final visual. Most of these issues will be solved by experimentation, and trying different things in the prototype until I get a glimpse of the true shape of this sculpture.
Do not despair however as I have made a start with some code that was needed as one of the first stepping stones. I needed to know that adding the high level LOD polygons to the visible range of the player's current position would not be too slow. I thought I had solved this before, but when I used a high polygon segment and then painted solid tiles with it in every direction, it started to crawl within a range of 5x4x5 which would be completely unworkable in the real world. Given these new performance measurements, I improved the situation by giving the polygon builder an early exit counter so it would not try to add all the polygons in one go. Instead, it will now add a few meshes to the buffer, then exit to resume other program tasks, and on the next cycle do a few more. This spread out the workload and I ensure the player cannot see this 'slower build' by capping the speed at which the player can move through the world, so you cannot effectively catch up to the edge of the world that is still being build (added to the buffers).
I also used SYNC RATE 0 to remove the artificial cap which was causing stutter with the above technique. Removing the sync cap solved the last of the glitches and I was able to run through the level at anywhere between 130fps and 700fps viewing full high polygon segments up to 10x5x10 which was a reasonable distance for the high polygon meshes.
Variable LOD Range and Nested Buffers
As I coded the above I also realised it would be super powerful if the range at which high lod turns to low lod should be variable and determined automatically by the load on the system. So when the engine detects lots of load (polygons in the buffers), it will bring in the LOD range to handle and control polygon and draw counts, and will extend it where the load is low allowing high polygon meshes to be left alone even when rendered at some distance.
The big code task is ahead of me though, and the technique I am mentally wrestling with is a nested buffer system which will make two passes of the scene to add one lod level and then the other. Separate planes of existence if you will, but overlapped to service the final render. I would then cut a hole into the low lod universe where there are high lod meshes handling the render at that location. I am still not happy about the approach as it leaves some questions open about how the two universes join, the additional performance drain of adding many more buffer writes each time the player moves, and what to do about all the texture render state changes as I attempt to render a potential 40x20x40 view in one go. An alternative was to create a huge universe of low lod and simply hide and show parts of it as the high level lod traverses through the reference data, but this worked out at over 10 million polygons and too many draw calls. I will stick with the first theory as that protects memory, render speed and is the most adaptable solution. I only hope the performance drain can be managed when the time comes. My ace in the hole fall-back is of course the fact not everyone will be filling every 200x20x200 tile grid with a high density segment object 87K in size. I imagine most levels will be large open terrain with walled cities of tightly pressed in buildings, or something :)
Signing Off
Normally I would have another two hours of coding, but it's just gone 5PM and the sun is shining so I am going to recharge out there for an hour, then chalk off the whole evening to creating some code which reflects the above theory. The only way to know is to do, and so that is my plan before the next blog.
A question regarding LOD: is it envisaged that the LOD system will be exclusively mesh based, or is it the intention that different texture maps/shading accuracies will be used as well?
ReplyDeleteAt the moment it's only mesh LOD. Once I see the texture activity, I can decide whether to create a LOD system for combining textures. Of course mipmapping will take care of textures in the distance but I have a mind to research adding distant object textures to a single plate, allowing me to use a single DX buffer to draw a distant LOD scene that comprises anything up to 64 textures in a single draw call. It's one of those deals where you have to do performance and memory tests before you know if you need to do more code :)
ReplyDeleteIf you're thinking texture atlas, just remember you'll have problems with texture bleeding when it comes to mipmaps.
ReplyDelete