Terrain stitching micro seams

This topic is 2013 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

Recommended Posts

I am experiencing a weird phenomenon.

I have implemented large scale terrain that is divided into chunks of various resolutions/LOD. When two different LOD chunks are near each other, I do stitching once on the CPU to fix the seam. The way I do it is it take two points from the higher resolution chunk that align up to the lower one and compute their average height. Then I set the middle vertex height to that average and apparently this fixes all discontinuities between chunks.

But something is amiss. It seams hat my simple average is not up on par with the way the hardware rasterizer triangles and sometimes a single pixel wide hole appears. These come and go as you move the camera around, often staying only for one frame.

http://dl.dropboxusercontent.com/u/45638513/ter08.png

In the above image I was lucky enough to capture two of the fleeting seams near each other. The red line is the LOD discontinuity. The two seams are circled in red. As I move around the camera, white pixel come and go, all found on the discontinuity line. Otherwise, when the pixels don't show up, there are no seams.

Any idea how to fix this? My far plane is pretty far away, may have something to do with it.

The only idea I have is to add extra triangles in that "seam".

Thank you!

Share on other sites

The infamous "T junction" problem:

A-C-B
|/ \|
D---E

You've got lines from A-B (through C) A-D, B-D, D-E, D-C and C-E.
The line from A-B is exactly equivalent to A-C and C-B, so everything should be fine...

...but it's not fine! Numerical precision is such that it will never line up exactly perfectly.
The solution is to... never use T-junctions. Just say no.

Either you delete the A-B edge, replacing it with true A-C and C-B edges.
Or, you delete C altogether, and just have A-B, A-D, D-B, B-E and D-E:

A---B
| / |
D---E

Share on other sites

ASCII diagrams. Good! Might make things clearer.

So I have the low LOD box XYZW and the high LOD box ACGI. Z = A and C = W. The two boxes are in separate vertex buffers:

  X---Y
|  /|
| / |
|/  |
Z-A-B-C-W
|/|/|
D-E-F
|/|/|
G-H-I


The seam is at the ZW - ABC common edge, so I tried to fix it making B = (Z.Y + W.Y) / 2. So you are saying that numeric precision is not good enough for this not to give the micro seams?

Share on other sites

So you are saying that numeric precision is not good enough for this not to give the micro seams?

Yes. In some cases (like where the height of the edge is constant) it will work, but in the general case, B will never be perfectly aligned with the LOD edge. It will be ever-so-slightly above or below the LOD edge. All joins between meshes need to happen at vertices - you can't (reliably) join two meshes by aligning a vertex with a face/edge, only by making it exactly aligned with another vertex.

Another fix is to move B sideways so that it overlaps with either A/Z or with C/W -- this is the same as eliminating B. Say you move B to overlap with C. The triangle formed by C-C-E ends up having zero area and disappearing (in GPU jargon, it's a "degenerate triangle"), but the A-B-D and B-E-D triangles will fill the gap.

Share on other sites

So you are saying that numeric precision is not good enough for this not to give the micro seams?

Yes. In some cases (like where the height of the edge is constant) it will work, but in the general case, B will never be perfectly aligned with the LOD edge. It will be ever-so-slightly above or below the LOD edge. All joins between meshes need to happen at vertices - you can't (reliably) join two meshes by aligning a vertex with a face/edge, only by making it exactly aligned with another vertex.

Another fix is to move B sideways so that it overlaps with either A/Z or with C/W -- this is the same as eliminating B. Say you move B to overlap with C. The triangle formed by C-C-E ends up having zero area and disappearing (in GPU jargon, it's a "degenerate triangle"), but the A-B-D and B-E-D triangles will fill the gap.

Thank you for the info!

I would like to avoid updating vertex buffers if possible. This is what I've done in the past and it is very hard to keep the system working in real time with fast movement speed.

I am going to try and add a new triangle A - C - B. Actually, I'll try and pad the entire chunk with such triangles and see how that works.

Then at runtime one can only select an a different index buffer to enable stitching. Might give some artifacts. I need tot test it out.

Share on other sites

i use a similar approach to create a 2500x2500 mile ground mesh ( ~ 4000km x 4000km ) at a scale of 1 foot = 1 d3d unit (~3d3d units per meter). but i'm not dealing with meshes whose vertices are non-coincident.

in such cases, an alternate approach to "zipping up the seams"  as i call it, is to keep them separate meshes that overlap slightly, and let the zbuffer take care of the rest.

but this can lead to z-fighting when far from the camera. a way to combat this is with a two pass method that renders distant stuff first with a far setting for the near and far planes, then close stuff with shorter settings for the near and far planes. the overhead of changing the projection matrix a couple times per frame is negligible.  i use a 3 pass method in Caveman, one far, one near, and a very close one for first person player weapon animations.

overlap means no seam fixup overhead too. i do my seam fixup when i generate static terrain chunk meshes on the fly as needed from the world map data structure. so overhead is not too bad. using dynamic meshes it would probably be much worse. i used to use dynamic meshes for the ground, but found that generating and using static chunks was MUCH faster.

Share on other sites

You can use a pregenerated set of index buffers in order to fix the t-junction problem, no need to touch the vertex data.

Otherwise, the easiest solution of all is to use tesselation hardware. Just give correct tesselation factors for each edges and the problem is solved. No need for index buffers (or even vertex buffers :D).

Cheers!

Share on other sites

You can use a pregenerated set of index buffers in order to fix the t-junction problem, no need to touch the vertex data.

Otherwise, the easiest solution of all is to use tesselation hardware. Just give correct tesselation factors for each edges and the problem is solved. No need for index buffers (or even vertex buffers ).

Cheers!

This is what I'm trying right now. Using the same vertex buffers I have multiple sets of index buffers with extra triangles at the LOD switch edge. This approach can and will generate ugly "extruded wings" terrain artifacts, but this is only visible in debug builds where I test small LOD ranges. In practice where the change occurs hundreds of meters away from the character I can't spot the wings yet. The first time I can spot one, I might reconsider this approach.

I'm on DirectX 9 so tessellation is out of the question. Since I'm so inexperienced in more advanced techniques, I even shied away from from keeping my terrain data in textured and having the vertex shader sample the textures, so I'm building and updating classical vertex buffers on the fly using a far too complicated poll system.

Anyway tessellation scares me . But I'd love one day to graduate by implementing something like this: http://www.vertexasylum.com/downloads/cdlod/cdlod_latest.pdf

i use a similar approach to create a 2500x2500 mile ground mesh ( ~ 4000km x 4000km ) at a scale of 1 foot = 1 d3d unit (~3d3d units per meter). but i'm not dealing with meshes whose vertices are non-coincident.

Wow, that seems big. I'm having problems mapping how many height data points you have there because of the units you are using.

I have a ~8 km * 8 km (64 square kilometer) map, with ~8000x8000 height data points. I am having troubles with so much data, but soon I will be able to update to 100 square kilometers. Then I'm planning a 256 one and that is going to be hard. Still several orders of magnitudes smaller than your map.

One problem that I'm facing with size is perception. The 64 square kilometers one looks like a puny patch of ground, not a big place of adventuring.

I am going to push the size up technically as big as possible, but there will come a point where I'll have to use some tricks to achieve the illusion of size. The first step is getting away from the 64 super small seeming map.

Share on other sites

I use a different method, illustrated here, to achieve almost infinite landscape, but it might not be to your tastes.

http://philliphamlyn.wordpress.com

An alternative I've seen used elsewhere is to drop a vertical "skirt" from all your tile edges, this is a bit of a cheat, but the user will never see the discontinuity.

Phillip

Share on other sites

Damn you T junctions! I think I managed to eliminate all holes and solved the problem for water tight terrain:

This is my first version with "infinite" view distance. It is not needed for such small terrains, but I'm rendering distant low res terrain first with 500/20000 near/far camera and then I clear the depth buffer and render the high res terrain LODs based on camera distance, closest first, with a 0.02/6000 near far camera.

The terrain still feels pretty small, but I am moving with 50 m/s in that video. With normal sprint speed it does take you 8 minutes to traverse one side of the terrain.

And something got lost in translation, because my old XNA version had slightly better looking terrain. This one is very gray and muddy.

Now I just need to populate that terrain with AAA maximum quality levels found in the year 2008!

• Game Developer Survey

We are looking for qualified game developers to participate in a 10-minute online survey. Qualified participants will be offered a \$15 incentive for your time and insights. Click here to start!

• 15
• 18
• 19
• 11
• 9