我一整天都在尝试让 Perlin 噪声生成正常工作,但我在执行 this tutorial 中的伪代码时遇到了问题。 .
this question 的答案中显示了类似的代码.
问题是我不知道 Hugo Elias 文章底部附近的 PerlinNoise_2D
函数中 x 和 y 的输入值应该是多少(或Total
函数(如果您查看的是较早的 Stack Overflow 问题)。
我有一个 500 x 500 的数组,我正在将像素值存储到其中,所以一开始我以为我只是应该遍历 PerlinNoise_2D
(或 Total
) 每个像素的函数,但这会导致我立即超出我的数组范围,因为第一个函数调用的代码使用索引 x - 1
,当然意味着当我给它我的第一个 x 索引(显然是 0)时,它会中断。
PerlinNoise_2D
(或 Total
)函数看起来确实像是 Perlin 类的预期入口点,而且它看起来也像是将返回值的函数我想要,但我终究无法弄清楚我应该传递什么,因为它绝对不是我的 x 和 y 像素数组索引。
有人知道我应该在这里传递什么吗?
您最初的假设是正确的,即函数希望您传入坐标对。在代码中看到 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
然后如果你打开上面的网页,你会看到这个图像: