2

I tried a quick and dirty translation of the code here.

However, my version outputs noise comparable to grey t-shirt material, or heather if it please you:

#include <fstream>
#include "perlin.h"

double Perlin::cos_Interp(double a, double b, double x)
{
  ft = x * 3.1415927;
  f = (1 - cos(ft)) * .5;

  return a * (1 - f) + b * f;
}

double Perlin::noise_2D(double x, double y)
{
  /*
  int n = (int)x + (int)y * 57;
  n = (n << 13) ^ n;
  int nn = (n * (n * n * 60493 + 19990303) + 1376312589) & 0x7fffffff;

  return 1.0 - ((double)nn / 1073741824.0);
  */
  int n = (int)x + (int)y * 57;
  n = (n<<13) ^ n;
  return ( 1.0 - ( (n * (n * n * 15731 + 789221) + 1376312589) & 0x7fffffff) / 1073741824.0);    
}

double Perlin::smooth_2D(double x, double y)
{
  corners = ( noise_2D(x - 1, y - 1) + noise_2D(x + 1, y - 1) + noise_2D(x - 1, y + 1) + noise_2D(x + 1, y + 1) ) / 16;
  sides   = ( noise_2D(x - 1, y) + noise_2D(x + 1, y) + noise_2D(x, y - 1) + noise_2D(x, y + 1) ) /  8;
  center  =  noise_2D(x, y) / 4;

  return corners + sides + center;
}

double Perlin::interp(double x, double y)
{
  int x_i = int(x);
  double x_left = x - x_i;

  int y_i = int(y);
  double y_left = y - y_i;

  double v1 = smooth_2D(x_i, y_i);
  double v2 = smooth_2D(x_i + 1, y_i);
  double v3 = smooth_2D(x_i, y_i + 1);
  double v4 = smooth_2D(x_i + 1, y_i + 1);

  double i1 = cos_Interp(v1, v2, x_left);
  double i2 = cos_Interp(v3, v4, x_left);

  return cos_Interp(i1, i2, y_left);
}

double Perlin::perlin_2D(double x, double y)
{
  double total = 0;
  double p = .25;
  int n = 1;

  for(int i = 0; i < n; ++i)
    {
      double freq =  pow(2, i);
      double amp = pow(p, i);

      total = total + interp(x * freq, y * freq) * amp;
    }

  return total;
}

int main()
{
  Perlin perl;
  ofstream ofs("./noise2D.ppm", ios_base::binary);

  ofs << "P6\n" << 512 << " " << 512 << "\n255\n";

  for(int i = 0; i < 512; ++i)
    {
      for(int j = 0; j < 512; ++j)
       {
         double n = perl.perlin_2D(i, j);

          n = floor((n + 1.0) / 2.0 * 255);

          unsigned char c = n;
          ofs << c << c << c;
       }
    }

  ofs.close();

  return 0;
}

I don't believe that I strayed too far from the aforementioned site's directions aside from adding in the ppm image generation code, but then again I'll admit to not fully grasping what is going on in the code.

As you'll see by the commented section, I tried two (similar) ways of generating pseudorandom numbers for noise. I also tried different ways of scaling the numbers returned by perlin_2D to RGB color values. These two ways of editing the code have just yielded different looking t-shirt material. So, I'm forced to believe that there's something bigger going on that I am unable to recognize.

Also, I'm compiling with g++ and the c++11 standard.

EDIT: Here's an example: https://i.stack.imgur.com/itTN2.jpg

genpfault
  • 51,148
  • 11
  • 85
  • 139
Stumbleine75
  • 391
  • 2
  • 7
  • 22
  • http://imgur.com/VDzF1tF is what it looks if you adjust the white point and blackpoint to the edges. The histogram looks like all the data is in a short range near the center of the values. What do you get with a 16x16 image, if you output the pure values to the console and the resulting PPM-data? – Skurmedel Jul 19 '14 at 15:02
  • @Skurmedel http://tny.cz/1168e215, http://imgur.com/6md0Eqe – Stumbleine75 Jul 19 '14 at 15:11
  • hmm, interesting, do you output the values straight without rescaling them to [0, 1]? – Skurmedel Jul 19 '14 at 15:20
  • @Skurmedel Yes, those numbers came directly from perl.perlin_2D(i, j) – Stumbleine75 Jul 19 '14 at 15:22
  • ok, I don't think PPM supports negative numbers. I'm also a bit unsure about your output, it seems to me they could be written out as 32-bit floating points. What happens if you convert `n` to uint8_t first? – Skurmedel Jul 19 '14 at 16:05
  • @Skurmedel I avoid negatives via 'n = n - floor(n);'. Also, I typecasted n to uint8_t right after calculation and that made all of the numbers zero. Anyway, I also thought the numbers were odd. It seems that all the numbers are concentrated toward the middle of the -1 1 range, hence the grey. – Stumbleine75 Jul 19 '14 at 16:12
  • ThroatOfWinter57 ah sorry, you are right. I've spotted an error though: ´double y_left = y = y_i;´ – Skurmedel Jul 19 '14 at 16:25
  • @Skurmedel Thanks for catching that! Messy code is messy. However, that fix didn't produce any noticeable changes. – Stumbleine75 Jul 19 '14 at 16:33
  • If you open the PPM in a text-editor, what do you get? I've got a feeling you get things like 234.0000 depending on your locale. If you have a look at http://netpbm.sourceforge.net/doc/ppm.html the binary PPM-format expect the pixel data to be pure bytes, and not text. In your case you write out text and not machine numbers. – Skurmedel Jul 19 '14 at 16:42
  • @Skurmedel I changed to ofs << n << n << n << endl; and got this http://tny.cz/878721e7 – Stumbleine75 Jul 19 '14 at 16:51
  • I think you mean "[heather](http://en.wikipedia.org/wiki/Heather_(fabric))" grey. It used to drive me crazy that I didn't have a word to describe that. – sh1 Jul 20 '14 at 18:18

2 Answers2

2

To convert a double in the range of [-1.0, 1.0] to an integer in range [0, 255]:

n = floor((n + 1.0) / 2.0 * 255.99);

To write it as a binary value to the PPM file:

ofstream ofs("./noise2D.ppm", ios_base::binary);

...

    unsigned char c = n;
    ofs << c << c << c;
Ross Ridge
  • 38,414
  • 7
  • 81
  • 112
  • Thank you. This mostly did the trick and I sort of understand why but one problem lingers: http://imgur.com/cFM97Z3. There is an obvious pattern present. Each pair (x, y) is unique so I don't see how repetition can occur unless somewhere the code the noise values are repeating due to overflow and/or related errors. – Stumbleine75 Jul 21 '14 at 23:06
  • You haven't declared `corners` `sides` and `center` in the function `smooth_2D`, so I assume they're declared in another scope. It would be better to define them as local variables that way it would be clear that they're correct type (`double`). – Ross Ridge Jul 22 '14 at 01:08
  • Sorry for poor design. Those are in the header file as doubles. – Stumbleine75 Jul 22 '14 at 03:20
  • 1
    Most people don't zoom that far out on Perlin noise and only concentrate on an area not much bigger than 20x20. The pattern is present because the noise function (noise_2d) adds the x and y together which means that any time x+37y = n repeats - (e.g. x + 37y = 0, x = 37y), it's going to come up with the same noise numbers. It's linear. Additionally, the noise function (n*n*n ...) itself will repeat eventually, though I'm not enough of a mathematician to tell you what its frequency is. Come up with something that is not a linear dependency between x and y. XOR your n by x or y or something. – ThisHandleNotInUse Jul 22 '14 at 07:46
  • So: (x+1) = 37(y+1), (x+2) = 37(y+2)... etc http://imageshack.com/a/img537/498/915d20.png – ThisHandleNotInUse Jul 22 '14 at 08:06
  • 1
    I would multiply by `255.99999` instead of `255`, otherwise `255` will be underrepresented in the output. – Mark Ransom Jul 22 '14 at 14:57
  • Well, apparently 255 will never be output either way. The actual range of the standard 2d perlin noise function is actually about [-.707,.707] according to this: http://www.gamedev.net/topic/285533-2d-perlin-noise-gradient-noise-range--/ – Ross Ridge Jul 22 '14 at 15:11
  • In any case, your suggestion is a good one, so I've updated my answer, but with a few less decimal places. I'm more worried about outputting a number that's slightly above 1.0 as 0 than having 255 underrepresented by 1%. – Ross Ridge Jul 22 '14 at 15:18
  • @ThisHandleNOtInUse Thanks, I get it now. Also, the vertical/horizontal bars you predicted appeared when I first tried XORing n by x and y individually. Things now seem to be totally fixed when I did n = (n<<13) ^ ((int)pow((int)x, 3) + (int)pow((int)y, 3)); – Stumbleine75 Jul 22 '14 at 17:08
  • To be honest, I'm not sure what to make of the noise function. It's not the "traditional" method for perlin noise which involves using unit vectors as a gradient. I'd recommend this source if you want to better understand the traditional form: http://staffwww.itn.liu.se/~stegu/simplexnoise/simplexnoise.pdf Keep in mind that you might want to use pointers for noise methods and swap them out. E.G. I intentionally did "vertical noise" to play around on my 3-d generator to make this (just an experiment to play with "biased" noise): http://imageshack.com/a/img536/9455/215f5b.png – ThisHandleNotInUse Jul 23 '14 at 05:58
0

Is this a direct copy of your code? You assigned an integer to what should be the Y fractional value - it's a typo and it will throw the entire noise algorithm off if you don't fix:

 double Perlin::interp(double x, double y)
 {
     int x_i = int(x);
     double x_left = x - x_i;

     int y_i = int(y);
     double y_left = y = y_i;  //This Should have a minus, not an "=" like the line above

   .....

 }

My guess is if you're successfully generating the bitmap with the proper color computation, you're getting vertical bars or something along those lines?

You also need to remember that the Perlin generator usually spits out numbers in the range of -1 to 1 and you need to multiply the resultant value as such:

value * 127 + 128 = {R, G, B}

to get a good grayscale image.

ThisHandleNotInUse
  • 1,135
  • 1
  • 10
  • 23