Perlin Noise Generation

I've been trying to get the Perlin Noise generation to work all day and am having trouble implementing the pseudocode in this tutorial .

Similar code is shown in the answer to this question .

The problem is, I have no idea what the input values ​​for x and y should be in the function PerlinNoise_2D

at the bottom of Hugo Elias's article (or the i and j values ​​in a function Total

if you're looking at the earlier question).

I have a 500 by 500 array into which I store my pixel values, so at first I thought I just had to go through PerlinNoise_2D

(or Total

) for each pixel, but that makes me go out of bounds of my array IMMEDIATELY because one of the first function calls has code that uses an index x - 1

, which of course means that when I give it my first index x (obviously 0), it breaks.

The function PerlinNoise_2D

(or Total

) definitely looks like the intended entry point for the Perlin class, and it also looks like a function that will return the values ​​I want, but I can't for the rest of my life figure out what I had to go through because these are definitely not my indices array of x and y pixels.

Does anyone know what I should be going through here?

+3


source to share


1 answer


You were correct in your original assumption that the function expects you to pass in a coordinate pair. Where you see x-1

in the code, this is just an intermediate value for calculating noise. This value is not used as an index in your array.

I have implemented pseudocode in the following C ++ program.

// Two-dimensional value noise based on Hugo Elias description:
//   http://freespace.virgin.net/hugo.elias/models/m_perlin.htm

#include <cstdio>
#include <cmath>
#include <cstdlib>
using namespace std;

int numX = 512,
    numY = 512,
    numOctaves = 7;
double persistence = 0.5;

#define maxPrimeIndex 10
int primeIndex = 0;

int primes[maxPrimeIndex][3] = {
  { 995615039, 600173719, 701464987 },
  { 831731269, 162318869, 136250887 },
  { 174329291, 946737083, 245679977 },
  { 362489573, 795918041, 350777237 },
  { 457025711, 880830799, 909678923 },
  { 787070341, 177340217, 593320781 },
  { 405493717, 291031019, 391950901 },
  { 458904767, 676625681, 424452397 },
  { 531736441, 939683957, 810651871 },
  { 997169939, 842027887, 423882827 }
};

double Noise(int i, int x, int y) {
  int n = x + y * 57;
  n = (n << 13) ^ n;
  int a = primes[i][0], b = primes[i][1], c = primes[i][2];
  int t = (n * (n * n * a + b) + c) & 0x7fffffff;
  return 1.0 - (double)(t)/1073741824.0;
}

double SmoothedNoise(int i, int x, int y) {
  double corners = (Noise(i, x-1, y-1) + Noise(i, x+1, y-1) +
                    Noise(i, x-1, y+1) + Noise(i, x+1, y+1)) / 16,
         sides = (Noise(i, x-1, y) + Noise(i, x+1, y) + Noise(i, x, y-1) +
                  Noise(i, x, y+1)) / 8,
         center = Noise(i, x, y) / 4;
  return corners + sides + center;
}

double Interpolate(double a, double b, double x) {  // cosine interpolation
  double ft = x * 3.1415927,
         f = (1 - cos(ft)) * 0.5;
  return  a*(1-f) + b*f;
}

double InterpolatedNoise(int i, double x, double y) {
  int integer_X = x;
  double fractional_X = x - integer_X;
  int integer_Y = y;
  double fractional_Y = y - integer_Y;

  double v1 = SmoothedNoise(i, integer_X, integer_Y),
         v2 = SmoothedNoise(i, integer_X + 1, integer_Y),
         v3 = SmoothedNoise(i, integer_X, integer_Y + 1),
         v4 = SmoothedNoise(i, integer_X + 1, integer_Y + 1),
         i1 = Interpolate(v1, v2, fractional_X),
         i2 = Interpolate(v3, v4, fractional_X);
  return Interpolate(i1, i2, fractional_Y);
}

double ValueNoise_2D(double x, double y) {
  double total = 0,
         frequency = pow(2, numOctaves),
         amplitude = 1;
  for (int i = 0; i < numOctaves; ++i) {
    frequency /= 2;
    amplitude *= persistence;
    total += InterpolatedNoise((primeIndex + i) % maxPrimeIndex,
        x / frequency, y / frequency) * amplitude;
  }
  return total / frequency;
}

int main(int argc, char** args) {
  if (argc >= 3) {
    numX = atoi(args[1]);
    numY = atoi(args[2]);
  }
  if (argc >= 4) {
    numOctaves = atoi(args[3]);
  }
  if (argc >= 5) {
    persistence = atof(args[4]);
  }
  if (argc >= 6) {
    primeIndex = atoi(args[5]) % maxPrimeIndex;
  }
  fprintf(stderr, "numX: %d, numY: %d, numOctaves: %d, persistence: %.5f, ",
      numX, numY, numOctaves, persistence);
  fprintf(stderr, "primeIndex: %d\n", primeIndex);
  printf("var rawNoise = [\n");
  for (int y = 0; y < numY; ++y) {
    for (int x = 0; x < numX; ++x) {
      double noise = ValueNoise_2D(x, y);
      if (x == 0) {
        printf("  [");
      }
      printf("%.5f", noise);
      if (x == numX-1) {
        printf("]");
        if (y == numY-1) {
          printf("\n];\n");
        } else {
          printf(",\n");
        }
      } else {
        printf(", ");
      }
    }
  }
  return 0;
}

      

This program takes up to five command line arguments. The first four arguments correspond to the parameters numX

, numY

, numOctaves

and persistence

respectively.

The fifth argument primeIndex

, an integer from 0 to 9, that determines which of the ten random number generators is called first. Thus, you can get ten different results after fixing the values ​​of the other four parameters.

The program output is a JavaScript array. If you save this output in a file named rawNoise.js

, you can load the following webpage to view the noise image.



<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
  <title> Demonstration of two-dimensional value noise </title>
<script src="rawNoise.js"></script>
<script>
  var ValueNoise = {
    noise: { raw: rawNoise }
  };
  ValueNoise.load = function () {
    var g = ValueNoise,
        raw = g.noise.raw,
        numR = g.numR = raw.length,
        numC = g.numC = raw[0].length,
        minValue = raw[0][0],
        maxValue = minValue;
    for (var r = 0; r < numR; ++r) {
      for (var c = 0; c < numC; ++c) {
        maxValue = Math.max(maxValue, raw[r][c]);
        minValue = Math.min(minValue, raw[r][c]);
      }
    }
    var valueSpread = maxValue - minValue;
    console.log(minValue, maxValue, valueSpread);
    var container = document.getElementById('display'),
        canvas = document.createElement('canvas'),
        context = canvas.getContext('2d'),
        imageData = context.createImageData(numC, numR),
        data = imageData.data;
    for (var r = 0; r < numR; ++r) {
      for (var c = 0; c < numC; ++c) {
        var value = raw[r][c],
            scaled = Math.round(255 * (value - minValue) / valueSpread),
            pos = r*4*numC + 4*c;
        data[pos] = data[pos+1] = data[pos+2] = scaled;
        data[pos+3] = 255;
      }
    }
    console.log(imageData);
    canvas.width = numC;
    canvas.height = numR;
    container.appendChild(canvas);
    context.putImageData(imageData, 0, 0);
  };
  window.onload = ValueNoise.load;
</script>
</head>
<body>
<div id="wrapper">

<div id="display"></div>

</div><!--end wrapper -->
</body>
</html>

      

On the Unix style command line, you can compile and run a C ++ program like this:

g++ -O2 noise.cpp -o noise
./noise 800 800 9 0.65 3 > rawNoise.js

      

Then if you open the above webpage you will see this image:

enter image description here

+3


source







All Articles