You were correct in your original supposition that the function expects you to pass in a coordinate pair. Where you see x-1 in the code, it's just an intermediate value for the noise calculation. That value isn't used as an index into your array.
I have implemented the pseudocode in the following C++ program.
// Two-dimensional value noise based on Hugo Elias's 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 accepts up to five arguments on the command line. The first four arguments correspond to the parameters numX, numY, numOctaves, and persistence, respectively.
The fifth argument is primeIndex, an integer from 0 to 9, which 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 output of the program is a JavaScript array. If you store this output in a file called rawNoise.js, you can load the following web page to view an image of the noise.
<!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 a Unix-style command line, you can compile and run the 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 web page, you'll see this image:
