Thursday, November 17, 2016

Victory Over the Swiss!

In yesterday's blog post, I described my battles with the Swiss turbulence (or erosive noise) Giliam de Carpentier describes in his blog post and publication.  An hour's work this evening has largely resolved the outstanding problems.  As I suspected, a bit of analysis of the algorithm for Perlin noise derivative calculation, and comparison with the algorithm for Perlin noise generation, allowed me to figure out what must be done.  After about 30 minutes of experimenting with the parameters, I was generating terrains with excellent mountain topography.

Seven octaves of Swiss turbulence, with gain 0.8, vertical scale 175, and a horizontal scale of 0.0003
There's a bit more that can be done to improve it, with some smoothing or alternate interpolation methods.  I imagine that adding a little bit of erosion, perhaps as part of a river model, will improve the appearance further.

I'll be on to other things for now.  I want to look at integrating some roads and settlements into the current "endless" dynamic terrain that I've been playing with in my engine test.  Dynamic road and building generation, even without complex city generation algorithms, should be possible.  Maybe basic Perlin noise as a lookup to indicate if terrain should be mountains, hills, or flatter land that is forest, field, or varying densities of development.

In case you're trying to implement this yourself and have run into problems because the gradients and permutation tables in the examples are implemented very differently than you've implemented Perlin noise, I've included a bit of my C# code below.  The solution to the problem was darn simple.  Using any of the examples I linked in yesterday's post, the calls to the myRandomMagic function are simply replaced calls to GetGradient.

private const int GradientSizeTable = 256;

private readonly double[] _gradients = new double[GradientSizeTable * 3];
/* Gradient table borrowed from code by Darwyn Peachey.
   The gradient table is indexed with an XYZ triplet, which is first turned
   into a single random index using a lookup in this table. The table simply
   contains all numbers in [0..255] in random order. */

private readonly byte[] _perm = new byte[] {
 225,155,210,108,175,199,221,144,203,116, 70,213, 69,158, 33,252,
5, 82,173,133,222,139,174, 27,  9, 71, 90,246, 75,130, 91,191,
 169,138,  2,151,194,235, 81,  7, 25,113,228,159,205,253,134,142,
 248, 65,224,217, 22,121,229, 63, 89,103, 96,104,156, 17,201,129,
  36,  8,165,110,237,117,231, 56,132,211,152, 20,181,111,239,218,
 170,163, 51,172,157, 47, 80,212,176,250, 87, 49, 99,242,136,189,
 162,115, 44, 43,124, 94,150, 16,141,247, 32, 10,198,223,255, 72,
  53,131, 84, 57,220,197, 58, 50,208, 11,241, 28,  3,192, 62,202,
  18,215,153, 24, 76, 41, 15,179, 39, 46, 55,  6,128,167, 23,188,
 106, 34,187,140,164, 73,112,182,244,195,227, 13, 35, 77,196,185,
  26,200,226,119, 31,123,168,125,249, 68,183,230,177,135,160,180,
  12,  1,243,148,102,166, 38,238,251, 37,240,126, 64, 74,161, 40,
 184,149,171,178,101, 66, 29, 59,146, 61,254,107, 42, 86,154,  4,
 236,232,120, 21,233,209, 45, 98,193,114, 78, 19,206, 14,118,127,
  48, 79,147, 85, 30,207,219, 54, 88,234,190,122, 95, 67,143,109,
 137,214,145, 93, 92,100,245,  0,216,186, 60, 83,105, 97,204, 52};

private int Permutate(int x)
{
const int mask = GradientSizeTable - 1;
return _perm[x & mask];
}

private int Index(int ix, int iy, int iz)
{
// Turn an XYZ triplet into a single gradient table index.
return Permutate(ix + Permutate(iy + Permutate(iz)));
}

private float GetGradient(int i, int j, int k)
{
int index = Index(i, j, k);
int g = index * 3;
return (float)_gradients[g];
}

No comments:

Post a Comment