1
\$\begingroup\$

The Marching Cubes (MC) algorithm works by taking 8 points of a cube with different density values and converting them into vertices and triangles based on a given iso level (threshold above which is considered solid area). It can create quite a smooth result. For example the following picture shows MC generating polygons using a 3D noise perlin function. enter image description here

However 3D perlin noise is not very useful for generating terrain. 2D perlin noise is much better. One could use the following function to generate the density values at each corner of each cube sent through the marching cubes algorithm with an iso level of 0:

float IsoValue(Vector3 pos) {
    float gap = maxHeight - minHeight;
    float perlin = (Mathf.PerlinNoise(pos.x * frequency, pos.z * frequency) * gap) + minHeight; 
    if(pos.y > perlin) {
        return -1f;
    }
    else {
        return 1f;
    }
}

It produces a jagged-looking result: enter image description here

This is because unlike the 3d-noise function, the density values go straight from 0 to 1, and MC always generates vertices along a grid instead of at different heights in between the grid. How do I modify the above IsoValue function to not always give values between 0 and 1 (to make smoother looking terrain) depending on how close the generated noise is to the grid?

\$\endgroup\$
1
  • \$\begingroup\$ I didn't figure out why using 2D noise was making a jagged result, but I found a better alternate solution of using 3D noise instead, and following the density function tutorial described in GPU Gems 3 \$\endgroup\$
    – epitaque
    Commented Jan 16, 2017 at 21:47

2 Answers 2

1
\$\begingroup\$

You need to set a positions density to

Mathf.clamp(perlin - pos.y, 0, 1);

Instead of setting the values to either 1 or 0, this makes the top a bit more smooth. The clamp is required to make range of thr values inside the mesh lie between 1 and 0.

You should always pass a value between 1 and 0 to the marching cubes algorithm

\$\endgroup\$
3
  • \$\begingroup\$ This doesn't really work, the terrain is still blocky/jagged. For reference on how smooth it should be [here](blob:imgur.com/a4f17869-cda0-414c-acba-b4d5ffc252da) is a mesh generated by the same MC algorithm except using a 3D perlin noise function. \$\endgroup\$
    – epitaque
    Commented Jan 16, 2017 at 18:07
  • \$\begingroup\$ i.imgur.com/8jgGl0a.png \$\endgroup\$
    – epitaque
    Commented Jan 16, 2017 at 18:12
  • \$\begingroup\$ You probably don't want the clamp function here. The interesting detail happens right at the 0-crossing, and your clamp is chopping off all negative values. \$\endgroup\$
    – DMGregory
    Commented Mar 24, 2018 at 17:53
0
\$\begingroup\$

Check out this GPU gems article an making nice looking terrain with marching cubes.

The trouble is that what you want is a continuous 3D function over which to compute the isosurface. In other words, you want a signed distance field representation of the map. Not a heightmap. 3D perlin noise is already representing a signed distance field in 3D, so it works with marching cubes.

Note that Marching Cubes needs continuous gradients in 3D to represent smooth surfaces. The function you provided does not have continuous gradients in 3D (there is a hard cutoff in x and y based on the height value, and no gradient in z).

In the GPU gems article, we get a nice way of generating a 3D signed distance field based on terrain which is mostly 2 dimensional. You simply add together the results from 3D perlin noise with a simple modulating function that goes from 0 to 1 from the minimum to maximum height of the world.

From the article:

For example, for a flat plane at a height of y = 0

 `float density = -ws.y;`

Then you can add noise to this simple density function:

density += noiseVol.Sample(TrilinearRepeat, ws).x;

Where in your case the "noiseVol.Sample" would be your 3D perlin noise function.

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .