Geomipmapping - solving cracks

Started by
13 comments, last by Megahertz 18 years, 10 months ago
Okay, so I've got the basics of geomipmapping up and running - i.e. the LOD is determined based on the error values that are calculated at load time. Now, of course, I have the usual problem that cracks appear between patches of different LOD. I know that I need to change the indices of the patch neighbours with lower detail. I understand how to do this, however, I have a question - what do other people do for this? Do you generate all possible configurations at loadtime? I.e., for all LOD, calculate the index configurations for all possible neighbour LODs. This is what I would like to do, since I don't want to be modifying my index buffer at runtime. Also, because I only use one index buffer for all patches, I don't think the memory requirements will be that high, even for all possible configurations. While I'm on the subject, how do other people write the functions which connect the patch to neighbouring patches? My code is currently bordering on being a massive "hardcoded" function, where I work out the indices myself - that seems easier than trying to compute what the indices should be.
Advertisement
Currently working on this problem myself.

You could just pregenerate the LOD's index buffers but personally I'm going to try and generate them on the fly when the LOD changes for a given patch and see how performance is. I'm hoping it wont be too cpu intensive but if it is, then I'll fall back to pregenerating them. It's just something I'm going to have to benchmark and see.

www.freeworld3d.org has some demo code available that tackles this problem and it's worth taking a look at. It's not a tutorial mind you, so you're just gonna hafta dig through the code and figure out what's going on.

The demo code is in the community section of the site (forums) and is under the Announcements forum.

Here's a link to the demo stuff

http://freeworld3d.org/Geomipmapping.zip

HTH
-=[ Megahertz ]=-
-=[Megahertz]=-
I dont really see a reason for generating them at runtime. If you limit the neighbour patch to being == the current lod level or current lod-1, then theres only 2^4=16 combinations per LOD level. So if you have like 4 or 5 lod levels, thats only 4 or 5 * 16 combinations, which isnt really alot. So thats something that is really worth precalculating because when you have all combinations, pretty much zero time is needed to switch an lod at runtime.
In my implementation I figure everything out when the terrain is generated, and each 'mip' or whatever the chunk of land is called keeps track of which of it's neighbors have a higher level of detail than it, and it'll show just the extra triangles on that edge only. It wasn't an easy task to really get dirty and figure out which triangles bordered which, but once I got rolling with a good set of tests to perform, it was all a matter of getting the logic straight in my head, and then in code.
When I was experimenting with terrain, I found that generating index buffers whenever the LOD of a patch (or its neighbors) changes performs just fine. I used the vertex skipping method for the edges so that a patch can connect to any neighbor with a lower level of detail... It's really easy to code too; for each edge vert you detect if it's going "in between" the vertices of the neighboring (lower-LOD) patch and then use the index of the nearest valid vertex. The resulting degenerate triangles aren't rendered by the hardware.
Thanks very much for your help guys!

I've gone for the "compute everything beforehand" approach. I have a patch size of 17, so I've got 5 LOD's. This means I have (4 * 16) plus 16 = 80 sets of indices. (The most detailed level only needs one set of indices).

I can't really comment on performance as I haven't implemented it any other way, so I can't compare.

Anyway, in case anybody's interested, here's the screenshots of what it looks like now (these two are taken from the same angle):





Right then, I guess I better implement some culling...
IMHO, skirts are a good way of fixing cracks between different LOD levels. They're easy to create and totally static so you only have to do minimal amounts of extra work at run time to fix the cracks. Depending on how else you fix cracks they're probably going to be less memory as well.
Quote:Original post by roastedamoeba
Thanks very much for your help guys!

I've gone for the "compute everything beforehand" approach. I have a patch size of 17, so I've got 5 LOD's. This means I have (4 * 16) plus 16 = 80 sets of indices. (The most detailed level only needs one set of indices).

I can't really comment on performance as I haven't implemented it any other way, so I can't compare.

Anyway, in case anybody's interested, here's the screenshots of what it looks like now (these two are taken from the same angle):





Right then, I guess I better implement some culling...


It seems in your images that you implemented the correct way for choosing the appropriate LOD level. Would you be willing to share your code for doing that ?
Author Freeworld3Dhttp://www.freeworld3d.org
Quote:Original post by OrangyTang
IMHO, skirts are a good way of fixing cracks

My god, someone who agrees with me about skirts! Worth mentioning though, that you really have to use a screenspace error metric for skirts to be reliably unnoticeable.

Quote:Original post by oconnellseanm
Would you be willing to share your code for doing that ?

Promit was kind enough to do so when I was trying to figure it out. See this thread.



[size="1"]
Quote:Original post by roastedamoeba
I've gone for the "compute everything beforehand" approach. I have a patch size of 17, so I've got 5 LOD's. This means I have (4 * 16) plus 16 = 80 sets of indices. (The most detailed level only needs one set of indices).


Much, much too small. Your max batch size is 512 tris. Slide up to at least 33x33...my tests found 65x65 to be optimal for my usage. Preserve the same 5 LoDs...even 4, if you want. The rest of the LoDs run well into the diminishing returns area. It's simply not productive to reduce an entire patch to a single quad.

As for the skirts people, would one of you please detail exactly how skirts work? My understanding leads me to believe that they're a pretty miserable hack, and I'd like to be on the same page as everyone else while discussing this.


My own code uses cached index generation. I modify the index buffers to link up correctly (I remove vertices rather than adding, and I impose no limits on neighbor LoD deltas). I've also written things that prevent LoD from changing by more than one step every .5s to prevent popping, so the frequency with which indices change is really quite low. I cache the indices for the visible patches, and update as needed. My caching mechanism at the moment is really quite crude, and indeed it happens to a fairly large chunk of memory anyway. I'm planning to write a more sophisticated LRU slot based cache later, but it's not a pressing something.

I guess my main thing is that I can cope with the patch levels of detail as they are, and I don't need to impose the rather common restriction that most people have; that is, that a neighbor's level of detail can differ by at most one level. I don't like that restriction, as it artificially increases the detail levels of patches.
SlimDX | Ventspace Blog | Twitter | Diverse teams make better games. I am currently hiring capable C++ engine developers in Baltimore, MD.

This topic is closed to new replies.

Advertisement