Marching Cubes, Triplanar Mapping, Multires Textures, and Lemur

As some of you may have seen, I spent this past weekend playing around with marching cubes related stuff. It’s almost ready to roll out into some releases. I will probably post a sample app first and then finish packaging it up.

In the mean time, here is a video of the latest. Basically, just eye candy showing off the various things put together plus a shedload of post-processing effects and a simple Lemur-based GUI to tweak it.

Video best in HD, 720p…

So, under it all is a simple marching cubes mesh generator on top of some multi-octave fractal noise. Pretty much straight out of the GPU Gems 3 book except this is done on the CPU.

On top of that there is a single material using triplanar mapping. Instead of straight mapping each plane, multiresolution texture blending is used with some additional fractal noise. This keeps the texture from repeating and lets them look nice from far away and also maintain detail up close. The upper surfaces are thresholded to produce a hard edge that is also run through fractal noise ‘for interest’.

The HUD GUI is just something quick I threw together… though I think I finally have the ‘glass’ style definition just about ready to include as a default option in Lemur proper.

I’m still not sure what I will do with all of this but it seems like there are options. I really just wanted this capability in my toolbox because there are a handful of side projects that could take advantage of it.

More to come soon…

16 Likes

Hi,
Just had a thought… would it be practical, from a performance point or if even possible, to use normal (paged) terrain quads for “rolling hills” and have this marching cubes on top in some more rocky/mountainous areas for cliffs/shapes/caves etc?
Worth a thought or am I crazy? (well crazier…)

Cheers,
-Radan.

Looks awesome! I was investigating this topic for a little bit a few days ago but this certainly gives me motivation to push myself to go do something :stuck_out_tongue:

I was wondering though, what is the performance like and if its good, are there any real drawbacks of this, because it seems like it would be awesome for just about anything :p?

@radanz said: Hi, Just had a thought.... would it be practical, from a performance point or if even possible, to use normal (paged) terrain quads for "rolling hills" and have this marching cubes on top in some more rocky/mountainous areas for cliffs/shapes/caves etc? Worth a thought or am I crazy? (well crazier...)

Cheers,
-Radan.

Yeah, the density field can come from anywhere. For example, I posted another video before of animated sine waves and bubbles of terrain.

@reveance said: Looks awesome! I was investigating this topic for a little bit a few days ago but this certainly gives me motivation to push myself to go do something :p

I was wondering though, what is the performance like and if its good, are there any real drawbacks of this, because it seems like it would be awesome for just about anything :p?

Which performance? Generating or rendering?

Rendering is not that much worse than anything else. There is some percentage of overly small triangles due to the way the algorithm works but raw performance so far seems no worse than any other type of terrain. The places you can optimize are less flexible. There are easily a dozen ways to optimize heightmaps that are completely unavailable to something like this.

The fragment shader is a little heavy. It takes lots of texture samples. The side benefit is that it only takes one shader and I don’t have to have texture coordinates or tangent buffers or anything. Just position, normal, and index.

Generation speed is ok, too. The fractal that generates the density field takes the longest. Presumably for some applications that could be done ahead of time. For this fractal on my system, a 64x128x64 chunk takes about 0.6 seconds to generate the density field and about 60 ms or so to generate the mesh. This could be sped up significantly by any density field storage model that could easily mark empty chunks. In fact, in the final version of this test app I will chunk vertically as well since often there is a very limited vertical span where geometry gets generated… similar to a height map.

2 Likes

How does it behave storage space wise? Eg for spase terrains?

Is it the video which limit the fps to 30 or is it really expensive to handle?

I see the terrain is really big when you go up but …maybe it just miss some paging things

It looks great anyway :slight_smile: thinking of a marching cube terrain system directly editable in the sdk :stuck_out_tongue:

@pspeed said: Which performance? Generating or rendering?

Rendering is not that much worse than anything else. There is some percentage of overly small triangles due to the way the algorithm works but raw performance so far seems no worse than any other type of terrain. The places you can optimize are less flexible. There are easily a dozen ways to optimize heightmaps that are completely unavailable to something like this.

The fragment shader is a little heavy. It takes lots of texture samples. The side benefit is that it only takes one shader and I don’t have to have texture coordinates or tangent buffers or anything. Just position, normal, and index.

Generation speed is ok, too. The fractal that generates the density field takes the longest. Presumably for some applications that could be done ahead of time. For this fractal on my system, a 64x128x64 chunk takes about 0.6 seconds to generate the density field and about 60 ms or so to generate the mesh. This could be sped up significantly by any density field storage model that could easily mark empty chunks. In fact, in the final version of this test app I will chunk vertically as well since often there is a very limited vertical span where geometry gets generated… similar to a height map.


Thanks for all that info, it sounds great, definitely opens up a world of possibilties!

When I was trying to look over this stuff a bit, I understood that it was hard to create sharp features due to the algorithm interpolating and thus making it smooth.

It seems like you still got quite some detail in the video, is this just because the cells are small, or are there other methods to achieve more detail when using marching cubes?

Also, am I correct in assuming that dual marching cubes takes a bit more time to generate, but results in less triangles, so you could effectively increase the sharpness of features by decreasing the cell size and still end up with not too many triangles?

@Empire Phoenix said: How does it behave storage space wise? Eg for spase terrains?

There is no storage in this. I just generate a big density field and turn it into a mesh.

The code is modular. The part that makes the density field (DensityField is just an interface) is totally separate from the part that turns it into a mesh.

I will be adding some rudimentary paging soon but I’m still not sure I understand the question fully. The stored ‘chunks’ would most likely be float arrays. Depending on how the world was chunked the empty ones could be easily marked. In Mythruna, for example, empty chunks are like 20 bytes or something. They load fast and are thrown away easily.

Because building the mesh requires a certain amount of random access… and because that’s the part you really want to be fast… it kind of limits how you hold the density field. Still, most of them would be empty and so “has data” and “has no data” would already reduce the memory footprint considerably.

@reveance said: Thanks for all that info, it sounds great, definitely opens up a world of possibilties!

When I was trying to look over this stuff a bit, I understood that it was hard to create sharp features due to the algorithm interpolating and thus making it smooth.

It seems like you still got quite some detail in the video, is this just because the cells are small, or are there other methods to achieve more detail when using marching cubes?

Also, am I correct in assuming that dual marching cubes takes a bit more time to generate, but results in less triangles, so you could effectively increase the sharpness of features by decreasing the cell size and still end up with not too many triangles?

Yeah, the down sides of regular marching cubes are that sharp edges depend on where the feature falls on the grid… and that in this case really tiny triangles might be generated. For my purposes, this was sufficient. The particular algorithm that is used to make the mesh is somewhat pluggable. (Unforuntately since I don’t have any other examples to integrate at the moment, I can’t be sure I’ve defined the mesh builder interface sufficiently but it’s a relatively safe assumption that DensityField in, mesh out, is an appropriate interface.)

I do hear that dual marching cubes can solve some of these problems. I haven’t looked at it in detail. I know it may have a few of its own issues, also.

For me, raw generation speed was my primary goal so the implementations I have currently are geared towards that.

Another thing, the density field fractal that I’m using is really chaotic. It leaves small features floating in the air, etc… I did this on purpose because it’s the best way to test. A calmer algorithm that was somewhere closer to a height field would be easier to optimize around because more assumptions could be made. I’m still trying to be ‘general purpose’ at this point.

2 Likes
@haze said: Is it the video which limit the fps to 30 or is it really expensive to handle?

I see the terrain is really big when you go up but …maybe it just miss some paging things

It looks great anyway :slight_smile: thinking of a marching cube terrain system directly editable in the sdk :stuck_out_tongue:

FRAPS is limiting the frames to 30 FPS. I run it with vsync on normally and mostly get 60 FPS unless I’m staring straight across most of the geometry. When I don’t it’s because of all of the post-processing which is pretty ridiculous. Any one of them is kind of expensive but all of them together are pretty crazy. :slight_smile: They look really nice, though.

There is no paging in this demo. I just load a huge bunch of data and turn it into a mesh: 384 x 384

I final implementation (maybe before I release it) will have a pager of some kind. I want to show an example of how to combine the parts in such a situation.

It’s so great to see so many marching cubes projects at the moment =D Your terrain looks very nice, good work! Can’t wait to see the final release with paging, any ETA? :wink:

How many textures can your terrain “contain”?

Are you planning to use this algorithm for Mythruna? :-o

@Metoo3432 said: It's so great to see so many marching cubes projects at the moment =D Your terrain looks very nice, good work! Can't wait to see the final release with paging, any ETA? :wink:

I worked a little on paging last night for a bit. Still no ETA but I have a design I don’t hate yet at least.

@Metoo3432 said: How many textures can your terrain "contain"?

At the algorithm level, it’s agnostic. The material I use supports texture + normal map for XZ plane, YZ plane, and the XY plane… and then two (hi-res and lo-res) for the top plane. But any material could be used and the mesh generate can use a vertex processor to setup its own separate buffers for texture coordinates or whatever (in the final design). This is effectively how the normals are generated anyway. There is an array-based DensityField implementation that uses trilinear interpolation that could probably also be used for ‘material type’ if there was an array of materials (sand, dirt, rock, whatever). The version I’m running only has one material “ground”.

@Metoo3432 said: Are you planning to use this algorithm for Mythruna? :-o

Not really. I may use it for some volumetric type stuff. Maybe for water/lava and stuff.

@pspeed

VERY cool, I look forward to seeing the code. Bonus points if you can manage flowing water ala “From Dust” or better; Adding/removing water from domain data.

Regardless, exciting work!

Hey, in regard to memory I mean that if the terrain is modifiable (i suspect so) these changes need to be stored/materialized, wich in turn need memory , wich in turns may limit the amount of world visible at the same time. So do you do anything intresting in this regard? or is the current visible terrain only manipulatable with its noise function?

@Empire Phoenix said: Hey, in regard to memory I mean that if the terrain is modifiable (i suspect so) these changes need to be stored/materialized, wich in turn need memory , wich in turns may limit the amount of world visible at the same time. So do you do anything intresting in this regard? or is the current visible terrain only manipulatable with its noise function?

The current visible terrain is turned into array-based terrain per chunk. If you keep that chunk array data around then you could modify it. I plan to add something like this to the demo. You’d have to find some way to store and reload it (real paging) but that’s no different than Mythruna or any other similar engines, really.

For example, the other video I posted of the animation was just plugging values into an array based density field. There was no formula based field at all.

Anyway, here is what it looks like to extract a set of array values from the larger land’s fractal-based volume:
[java]
ArrayDensityVolume volume = ArrayDensityVolume.extract(landVolume, x, y, z, cx+3, cy+3, cz+3);
[/java]

But that’s the slowest (by far) part of the process so it would make sense to cache those to disk as they are encountered… especially if they will be modified after.

For proper local accuracy in the pager, I’m switching camera movement over to a 'conveyor" style system. Basically, I move the world instead of moving the camera.

Because the textures are not based on texture coordinates but on world position, this does cool stuff to the textures. I knew this would happen but I suspected it would look kind of cool and I was right. So before I updated the shaders to deal with this, I snapped a video.

If you look closely then you can maybe see a little bit of how the layering and noise-based distortion are combined to make for non-repeating textures. You can also see the noise-based function that is used for the hard edges on the grass.

[video]Layer Movement - YouTube

Now to add a world offset to the shader…

1 Like

Ok, since it’s taking me longer to clean this up and package it than I wanted… I’m posting a version as it is now:
http://mythruna.com/temp/IsoSurface/IsoSurfaceDemo-Windows.zip
http://mythruna.com/temp/IsoSurface/IsoSurfaceDemo-Linux.zip
http://mythruna.com/temp/IsoSurface/IsoSurfaceDemo-MacOSX.zip

The shader is still a bit of a mess of commented out and tried things, etc… no point cleaning it up until I see if it works for some other people also.

There is no paging in here. The terrain that’s loaded when you start is all you get right now. That’s what I’m working on at the moment but I got side tracked by a different issue.

Running
WASD moves
Mouse looks
QZ moves up and down relative to look direction
Space - toggles the mouse cursor on and off (ie: toggles the camera control on and off) letting you mess with the GUI.
Shift - fly faster

I’ve turned the shadow filter off by default because for some reason it crashes the app for me when I run this as an .exe. (From the IDE it’s fine until I load a model.) I have no idea of this is machine specific or not. For me, it will also happen in the IDE version if I load a certain model (I don’t know if it’s model-specific)… even if I don’t actually add the model to the scene. So something is funky.

In the HUD, most things work like you’d expect. The little glowing orbs in the right side of the title sections will toggle the filter. Clicking on a title section collapses that section.

If any of you get to try it: I hope it works! :slight_smile:

Edit: added mention of “shift” to fly faster.

2 Likes

Running now, with default options I get 20-30 fps (win7 32-bit, core2duo 2.67Ghz, nVidia GT630, windowed 1280x720)

Turning on shadows terminates with error:

Uncaught exception thrown in Thread[LWJGL Renderer Thread,5,main] IllegalStateException: Framebuffer has erronous attachment.

trying some more…

Cheers,
-Radan.

@radanz said: Running now, with default options I get 20-30 fps (win7 32-bit, core2duo 2.67Ghz, nVidia GT630, windowed 1280x720)

Turning on shadows terminates with error:

trying some more…

Cheers,
-Radan.


same ><