2
\$\begingroup\$

So, I'm learning how to generate terrain using Perlin Noise, and I came across this code: https://stackoverflow.com/questions/4753055/perlin-noise-generation-for-terrain/4753123#4753123

PerlinNoise.h

    #pragma once

class PerlinNoise
{
public:

  // Constructor
    PerlinNoise();
    PerlinNoise(double _persistence, double _frequency, double _amplitude, int _octaves, int _randomseed);

  // Get Height
    double GetHeight(double x, double y) const;

  // Get
  double Persistence() const { return persistence; }
  double Frequency()   const { return frequency;   }
  double Amplitude()   const { return amplitude;   }
  int    Octaves()     const { return octaves;     }
  int    RandomSeed()  const { return randomseed;  }

  // Set
  void Set(double _persistence, double _frequency, double _amplitude, int _octaves, int _randomseed);

  void SetPersistence(double _persistence) { persistence = _persistence; }
  void SetFrequency(  double _frequency)   { frequency = _frequency;     }
  void SetAmplitude(  double _amplitude)   { amplitude = _amplitude;     }
  void SetOctaves(    int    _octaves)     { octaves = _octaves;         }
  void SetRandomSeed( int    _randomseed)  { randomseed = _randomseed;   }

private:

    double Total(double i, double j) const;
    double GetValue(double x, double y) const;
    double Interpolate(double x, double y, double a) const;
    double Noise(int x, int y) const;

    double persistence, frequency, amplitude;
    int octaves, randomseed;
};

PerlinNoise.cpp

#include "PerlinNoise.h"

PerlinNoise::PerlinNoise()
{
  persistence = 0;
  frequency = 0;
  amplitude  = 0;
  octaves = 0;
  randomseed = 0;
}

PerlinNoise::PerlinNoise(double _persistence, double _frequency, double _amplitude, int _octaves, int _randomseed)
{
  persistence = _persistence;
  frequency = _frequency;
  amplitude  = _amplitude;
  octaves = _octaves;
  randomseed = 2 + _randomseed * _randomseed;
}

void PerlinNoise::Set(double _persistence, double _frequency, double _amplitude, int _octaves, int _randomseed)
{
  persistence = _persistence;
  frequency = _frequency;
  amplitude  = _amplitude;
  octaves = _octaves;
  randomseed = 2 + _randomseed * _randomseed;
}

double PerlinNoise::GetHeight(double x, double y) const
{
  return amplitude * Total(x, y);
}

double PerlinNoise::Total(double i, double j) const
{
    //properties of one octave (changing each loop)
    double t = 0.0f;
    double _amplitude = 1;
    double freq = frequency;

    for(int k = 0; k < octaves; k++) 
    {
        t += GetValue(j * freq + randomseed, i * freq + randomseed) * _amplitude;
        _amplitude *= persistence;
        freq *= 2;
    }

    return t;
}

double PerlinNoise::GetValue(double x, double y) const
{
    int Xint = (int)x;
    int Yint = (int)y;
    double Xfrac = x - Xint;
    double Yfrac = y - Yint;

  //noise values
  double n01 = Noise(Xint-1, Yint-1);
  double n02 = Noise(Xint+1, Yint-1);
  double n03 = Noise(Xint-1, Yint+1);
  double n04 = Noise(Xint+1, Yint+1);
  double n05 = Noise(Xint-1, Yint);
  double n06 = Noise(Xint+1, Yint);
  double n07 = Noise(Xint, Yint-1);
  double n08 = Noise(Xint, Yint+1);
  double n09 = Noise(Xint, Yint);

  double n12 = Noise(Xint+2, Yint-1);
  double n14 = Noise(Xint+2, Yint+1);
  double n16 = Noise(Xint+2, Yint);

  double n23 = Noise(Xint-1, Yint+2);
  double n24 = Noise(Xint+1, Yint+2);
  double n28 = Noise(Xint, Yint+2);

  double n34 = Noise(Xint+2, Yint+2);

    //find the noise values of the four corners
    double x0y0 = 0.0625*(n01+n02+n03+n04) + 0.125*(n05+n06+n07+n08) + 0.25*(n09);  
    double x1y0 = 0.0625*(n07+n12+n08+n14) + 0.125*(n09+n16+n02+n04) + 0.25*(n06);  
    double x0y1 = 0.0625*(n05+n06+n23+n24) + 0.125*(n03+n04+n09+n28) + 0.25*(n08);  
    double x1y1 = 0.0625*(n09+n16+n28+n34) + 0.125*(n08+n14+n06+n24) + 0.25*(n04);  

    //interpolate between those values according to the x and y fractions
    double v1 = Interpolate(x0y0, x1y0, Xfrac); //interpolate in x direction (y)
    double v2 = Interpolate(x0y1, x1y1, Xfrac); //interpolate in x direction (y+1)
    double fin = Interpolate(v1, v2, Yfrac);  //interpolate in y direction

    return fin;
}

double PerlinNoise::Interpolate(double x, double y, double a) const
{
    double negA = 1.0 - a;
  double negASqr = negA * negA;
    double fac1 = 3.0 * (negASqr) - 2.0 * (negASqr * negA);
  double aSqr = a * a;
    double fac2 = 3.0 * aSqr - 2.0 * (aSqr * a);

    return x * fac1 + y * fac2; //add the weighted factors
}

double PerlinNoise::Noise(int x, int y) const
{
    int n = x + y * 57;
    n = (n << 13) ^ n;
  int t = (n * (n * n * 15731 + 789221) + 1376312589) & 0x7fffffff;
    return 1.0 - double(t) * 0.931322574615478515625e-9;/// 1073741824.0);
}

The properties I decided to use were:

  • persistence = 1.0
  • frequency = 0.1
  • amplitude = 2.0
  • octaves = 1.0
  • randomseed = 1.0

Once I was able to get it running, I noticed a peculiar bug; plugging in these pairs creates a peculiar break in the terrain:

x | z

39 | 39

39 | 40

40 | 39

40 | 40

I then noticed that it appears the terrain creates a border, and generates a new seeded terrain. These new seeds begin at x = +40, and z = +40. Toying around with different seeds, I get the same effect. Sometimes I get multitudes of 7x7 squares that are completely different seeds. I'm not really sure what's happening. At first I thought there was some sort of 3 dimensional tangent going on, but I don't see any trig functions in the code.

This bug appears to occur no matter how I alter the properties.

I realize this is more of a programming question, but i was hoping to find someone more math oriented to see if they could deduce why my terrain (think 3D graph) has breaks in it. It's like they switch to a different method once I plug in more numbers.

Here's an example of my terrain bug. Im using these properties this time:

  • persistence = 1.1
  • frequency = 0.002
  • amplitude = 10.0
  • octaves = 6.0
  • randomseed = 1.0

and the break happens at x = 77, z = 77

https://dl.dropbox.com/u/25125155/noisebug.png

\$\endgroup\$
2
  • 2
    \$\begingroup\$ it would have been nicer if you attached some screenshots. even if you don't have enough reputation, you can just upload them and put links in your question. \$\endgroup\$
    – Ali1S232
    Commented Feb 17, 2013 at 4:30
  • \$\begingroup\$ added a link at the bottom of the post, as well as some updated info directly above it. \$\endgroup\$ Commented Feb 17, 2013 at 17:54

1 Answer 1

2
\$\begingroup\$

The problem may lie here:

int Xint = (int)x;
int Yint = (int)y;

You want the floor value instead, or you will get discontinuities near zero:

int Xint = (int)floor(x);
int Yint = (int)floor(y);
\$\endgroup\$
3
  • \$\begingroup\$ Weird... you'd think that the fact it's an interview would automatically floor the value, since it just trims of the decimal, but I guess not. That totally fixed it! Thanks man! \$\endgroup\$ Commented Feb 18, 2013 at 2:55
  • \$\begingroup\$ Int*... dang autocorrect. I would gladly upvote you, but i lack the reputation. \$\endgroup\$ Commented Feb 18, 2013 at 3:11
  • \$\begingroup\$ Conversion to integer gives the integer in the direction of zero. floor gives the integer towards negative infinity. ceil gives the integer towards positive infinity. Integral conversion is like floor for positive numbers and like ceil for negative numbers. \$\endgroup\$ Commented Feb 18, 2013 at 3:27

You must log in to answer this question.

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