我一直在努力让Perlin Noise生成一整天,我在this tutorial中实现伪代码时遇到了麻烦。
this question的答案中显示了类似的代码。
麻烦的是我不知道x和y的输入值应该在Hugo Elias文章底部附近的PerlinNoise_2D
函数中(或{{中的i和j值) 1}}函数,如果您正在查看早期的Stack Overflow问题)。
我有一个500 x 500的数组,我正在存储我的像素值,所以起初我以为我应该循环遍历Total
(或PerlinNoise_2D
)函数为每个每个像素,但这导致我立即超出我的数组的范围,因为第一个函数调用之一具有使用索引Total
的代码,这当然意味着当我给它我的第一个x索引(0 ,显然),它打破了。
x - 1
(或PerlinNoise_2D
)函数看起来绝对是Perlin类的预期入口点,它看起来也像是能够回复我想要的值的函数,但我可以为了我的生活,弄清楚我应该传递的是什么,因为它肯定不是我的x和y像素数组索引。
有人知道我应该在这里传递什么吗?
答案 0 :(得分:3)
你原来的假设是正确的,函数希望你传入一个坐标对。您在代码中看到x-1
的位置,它只是噪声计算的中间值。该值不能用作数组的索引。
我在以下C ++程序中实现了伪代码。
// 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;
}
该程序在命令行上最多接受五个参数。前四个参数分别对应参数numX
,numY
,numOctaves
和persistence
。
第五个参数是primeIndex
,一个从0到9的整数,它确定首先调用十个随机数生成器中的哪一个。因此,在修复其他四个参数的值后,您可以得到十个不同的结果。
程序的输出是一个JavaScript数组。如果将此输出存储在名为rawNoise.js
的文件中,则可以加载以下网页以查看噪声图像。
<!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>
在Unix风格的命令行中,您可以编译并运行C ++程序,如下所示:
g++ -O2 noise.cpp -o noise
./noise 800 800 9 0.65 3 > rawNoise.js
然后,如果您打开上述网页,则会看到此图片: