如何增加 Perlin 噪声分布的标准偏差
How to increase the standard deviation of a Perlin noise distribution
我一直在关注柏林噪声指南:http://flafla2.github.io/2014/08/09/perlinnoise.html
我使用的代码和他描述的完全一样,虽然我的渲染实现不同。但是,当我从该分布中获取噪声时,我倾向于在 0.5 范围附近获取很大一部分值,而在边缘处获取的值很少。您可以从我在下图中转换成快速图形的示例中看到这一点。我按屏幕宽度缩放噪声函数,并让它在每次生成噪声值时将 index = noise
处的数组递增 1。如您所见,分布几乎不是正态分布,因为边缘完全消失了。 (从技术上讲,它是没有标签和条形线的直方图)
使此分布向底部变宽而不向顶部变宽的最佳方法是什么?我希望大部分值位于高斯分布中,但我不能只使用高斯分布随机数,因为我需要选择彼此靠近的值以彼此接近(柏林噪声)。
我想更好的提问方式是:为什么我在范围的上四分之一和下四分之一处没有噪声值?有没有办法确定一个好的比例因子来扩大图表?
这是我用来获取图像的代码:
/**
* Simple linear interpolation
* @param a Start
* @param b End
* @param weight weighting
* @return A linear interpolation between points a and b
*/
public double lerp(double a, double b, double weight) {
return a + weight*(b-a);
}
/**
* Calculates a dot product between a distance vector and a pseudorandom
* "gradient" vector which gets picked using the hash
* @param hash
* @param x distance vector x component
* @param y distance vector y component
* @param z distance vector z component (z = 0 for 2D map)
* @return dot product of <x, y, z> and a pseudorandom gradient vector
*/
public double grad(int hash, double x, double y, double z) {
switch(hash & 0xF)
{
case 0x0: return x + y;
case 0x1: return -x + y;
case 0x2: return x - y;
case 0x3: return -x - y;
case 0x4: return x + z;
case 0x5: return -x + z;
case 0x6: return x - z;
case 0x7: return -x - z;
case 0x8: return y + z;
case 0x9: return -y + z;
case 0xA: return y - z;
case 0xB: return -y - z;
case 0xC: return y + x;
case 0xD: return -y + z;
case 0xE: return y - x;
case 0xF: return -y - z;
default: return 0; // never happens
}
}
/**
* A fifth order fade function: 6t^5 - 15t^4 + 10t^3
* @param t The x-value along the function, t is in [0, 1]
* @return The y-value for the fade function
*/
public double fade(double t) {
return t * t * t * (t * (t * 6 - 15) + 10);
}
//repeat is set to 0 so this method is just a regular "increment by 1"
public int inc(int num) {
num++;
if(repeat > 0) num %= repeat;
return num;
}
/**
* Generates a noise value in the range [0,1].
* Each coordinate is a given distance from a pseudorandomly picked set of
* gradient vectors. The vectors are determined by an array of 256 indexes,
* so the noise pattern inevitably repeats at a scale greater than 255,
* which is bigger than we should need.
* @param x
* @param y
* @param z
* @return
*/
public double perlin(double x, double y, double z) {
if(repeat > 0) {
x = x%repeat;
y = y%repeat;
z = z%repeat;
}
int xi = (int)x & 255;
int yi = (int)y & 255;
int zi = (int)z & 255;
double xf = x-(int)x;
double yf = y-(int)y;
double zf = z-(int)z;
double u = fade(xf);
double v = fade(yf);
double w = fade(zf);
int aaa, aba, aab, abb, baa, bba, bab, bbb;
aaa = p[p[p[ xi ]+ yi ]+ zi ];
aba = p[p[p[ xi ]+inc(yi)]+ zi ];
aab = p[p[p[ xi ]+ yi ]+inc(zi)];
abb = p[p[p[ xi ]+inc(yi)]+inc(zi)];
baa = p[p[p[inc(xi)]+ yi ]+ zi ];
bba = p[p[p[inc(xi)]+inc(yi)]+ zi ];
bab = p[p[p[inc(xi)]+ yi ]+inc(zi)];
bbb = p[p[p[inc(xi)]+inc(yi)]+inc(zi)];
double x1, x2, y1, y2;
/*
Box has corners:
____
|ab|
|cd|
----
Interpolate a-b, then c-d then both of those together, then repeat on the z-1 level
*/
x1 = lerp(grad (aaa, xf , yf , zf), // The gradient function calculates the dot product between a pseudorandom
grad (baa, xf-1, yf , zf), // gradient vector and the vector from the input coordinate to the 8
u); // surrounding points in its unit cube.
x2 = lerp(grad (aba, xf , yf-1, zf),
grad (bba, xf-1, yf-1, zf),
u);
y1 = lerp(x1, x2, v);
x1 = lerp(grad (aab, xf , yf , zf-1),
grad (bab, xf-1, yf , zf-1),
u);
x2 = lerp(grad (abb, xf , yf-1, zf-1),
grad (bbb, xf-1, yf-1, zf-1),
u);
y2 = lerp (x1, x2, v);
return (lerp (y1, y2, w)+1)/2; //Interpolate everything again and move the range from [-1, 1] to [0, 1]
}
/**
* Layers levels of noise, each with decreasing amplitudes and persistence
* @param x
* @param y
* @param z
* @param octaves
* @param persistence how much each layer impacts the layer below it
* @return
*/
public double octave(double x, double y, double z, int octaves, double persistence) {
double total = 0, frequency = 1, amplitude = 1, maxValue = 0;
for(int i = 0; i < octaves; i++) {
total += perlin(x * frequency, y * frequency, z * frequency) * amplitude;
maxValue += amplitude;
amplitude *= persistence;
frequency *= 2;
}
return total/maxValue;
}
这里是启动方法:
public void enter() {
eOffsetX = r.nextInt(10000);
eOffsetY = r.nextInt(10000); //This will "randomize" the seed of the noise
p = new int[512];
for(int x = 0; x < 512; x++) {
p[x] = permutation[x%256]; //Fill twice
}
eNoise = new float[(int)(1280/tile)][(int)(800/tile)];
gauss = new float[1280];
Arrays.fill(gauss, 0);
for(int i = 0; i < eNoise.length; i++) {
for(int j = 0; j < eNoise[0].length; j++) {
eNoise[i][j] = 1f * 100 * (float) octave(((double)i*zoom+eOffsetX)/1280, ((double)j*zoom+eOffsetY)/800, 0, 7, 0.60);
gauss[(int)(((eNoise[i][j]/100)-.5)*1280*2.5+640)] += 1f;
}
}}
渲染方法:
public void render(Graphics g) {
g.setBackground(Color.white);
g.setColor(Color.black);
for(int k = 0; k < 1280; k++) {
g.fillRect(k, 800-(gauss[k]/10), 5, 5);
}
}
最后,enter() 中提到的排列集:
private static final int[] permutation = { 151,160,137,91,90,15,
131,13,201,95,96,53,194,233,7,225,140,36,103,30,69,142,8,99,37,240,21,10,23,
190, 6,148,247,120,234,75,0,26,197,62,94,252,219,203,117,35,11,32,57,177,33,
88,237,149,56,87,174,20,125,136,171,168, 68,175,74,165,71,134,139,48,27,166,
77,146,158,231,83,111,229,122,60,211,133,230,220,105,92,41,55,46,245,40,244,
102,143,54, 65,25,63,161, 1,216,80,73,209,76,132,187,208, 89,18,169,200,196,
135,130,116,188,159,86,164,100,109,198,173,186, 3,64,52,217,226,250,124,123,
5,202,38,147,118,126,255,82,85,212,207,206,59,227,47,16,58,17,182,189,28,42,
223,183,170,213,119,248,152, 2,44,154,163, 70,221,153,101,155,167, 43,172,9,
129,22,39,253, 19,98,108,110,79,113,224,232,178,185, 112,104,218,246,97,228,
251,34,242,193,238,210,144,12,191,179,162,241, 81,51,145,235,249,14,239,107,
49,192,214, 31,181,199,106,157,184, 84,204,176,115,121,50,45,127, 4,150,254,
138,236,205,93,222,114,67,29,24,72,243,141,128,195,78,66,215,61,156,180
};
这是从0到255的所有数字,顺序随机。
同样,重申一下这个问题,该函数生成了 0.45 到 0.55 之间的大量数字,几乎没有超出该范围的数字。我想重新缩放函数,以便在较低和较高范围内获得更多数字。这可能发生在 perlin()
函数最后一行的某处,但我不确定该怎么做。感谢您的帮助。
我已经通过以下方法解决了这个问题。
首先,在生成噪声值时,我记录了最小值和最大值。
其次,在将噪声值存储到我的数组中时,我对数据进行了标准化:
(值 - 最小值)/(最大 - 最小值)。这样,我所有的值都在 0 到 1 之间,但分布得更好。该等式将最小值映射到 (min - min)/(max - min) 或 0,并将最大值映射到 (max - min)/(max - min) 或 1,并缩放介于两者之间的所有值。
我一直在关注柏林噪声指南:http://flafla2.github.io/2014/08/09/perlinnoise.html
我使用的代码和他描述的完全一样,虽然我的渲染实现不同。但是,当我从该分布中获取噪声时,我倾向于在 0.5 范围附近获取很大一部分值,而在边缘处获取的值很少。您可以从我在下图中转换成快速图形的示例中看到这一点。我按屏幕宽度缩放噪声函数,并让它在每次生成噪声值时将 index = noise
处的数组递增 1。如您所见,分布几乎不是正态分布,因为边缘完全消失了。 (从技术上讲,它是没有标签和条形线的直方图)
使此分布向底部变宽而不向顶部变宽的最佳方法是什么?我希望大部分值位于高斯分布中,但我不能只使用高斯分布随机数,因为我需要选择彼此靠近的值以彼此接近(柏林噪声)。
我想更好的提问方式是:为什么我在范围的上四分之一和下四分之一处没有噪声值?有没有办法确定一个好的比例因子来扩大图表?
这是我用来获取图像的代码:
/**
* Simple linear interpolation
* @param a Start
* @param b End
* @param weight weighting
* @return A linear interpolation between points a and b
*/
public double lerp(double a, double b, double weight) {
return a + weight*(b-a);
}
/**
* Calculates a dot product between a distance vector and a pseudorandom
* "gradient" vector which gets picked using the hash
* @param hash
* @param x distance vector x component
* @param y distance vector y component
* @param z distance vector z component (z = 0 for 2D map)
* @return dot product of <x, y, z> and a pseudorandom gradient vector
*/
public double grad(int hash, double x, double y, double z) {
switch(hash & 0xF)
{
case 0x0: return x + y;
case 0x1: return -x + y;
case 0x2: return x - y;
case 0x3: return -x - y;
case 0x4: return x + z;
case 0x5: return -x + z;
case 0x6: return x - z;
case 0x7: return -x - z;
case 0x8: return y + z;
case 0x9: return -y + z;
case 0xA: return y - z;
case 0xB: return -y - z;
case 0xC: return y + x;
case 0xD: return -y + z;
case 0xE: return y - x;
case 0xF: return -y - z;
default: return 0; // never happens
}
}
/**
* A fifth order fade function: 6t^5 - 15t^4 + 10t^3
* @param t The x-value along the function, t is in [0, 1]
* @return The y-value for the fade function
*/
public double fade(double t) {
return t * t * t * (t * (t * 6 - 15) + 10);
}
//repeat is set to 0 so this method is just a regular "increment by 1"
public int inc(int num) {
num++;
if(repeat > 0) num %= repeat;
return num;
}
/**
* Generates a noise value in the range [0,1].
* Each coordinate is a given distance from a pseudorandomly picked set of
* gradient vectors. The vectors are determined by an array of 256 indexes,
* so the noise pattern inevitably repeats at a scale greater than 255,
* which is bigger than we should need.
* @param x
* @param y
* @param z
* @return
*/
public double perlin(double x, double y, double z) {
if(repeat > 0) {
x = x%repeat;
y = y%repeat;
z = z%repeat;
}
int xi = (int)x & 255;
int yi = (int)y & 255;
int zi = (int)z & 255;
double xf = x-(int)x;
double yf = y-(int)y;
double zf = z-(int)z;
double u = fade(xf);
double v = fade(yf);
double w = fade(zf);
int aaa, aba, aab, abb, baa, bba, bab, bbb;
aaa = p[p[p[ xi ]+ yi ]+ zi ];
aba = p[p[p[ xi ]+inc(yi)]+ zi ];
aab = p[p[p[ xi ]+ yi ]+inc(zi)];
abb = p[p[p[ xi ]+inc(yi)]+inc(zi)];
baa = p[p[p[inc(xi)]+ yi ]+ zi ];
bba = p[p[p[inc(xi)]+inc(yi)]+ zi ];
bab = p[p[p[inc(xi)]+ yi ]+inc(zi)];
bbb = p[p[p[inc(xi)]+inc(yi)]+inc(zi)];
double x1, x2, y1, y2;
/*
Box has corners:
____
|ab|
|cd|
----
Interpolate a-b, then c-d then both of those together, then repeat on the z-1 level
*/
x1 = lerp(grad (aaa, xf , yf , zf), // The gradient function calculates the dot product between a pseudorandom
grad (baa, xf-1, yf , zf), // gradient vector and the vector from the input coordinate to the 8
u); // surrounding points in its unit cube.
x2 = lerp(grad (aba, xf , yf-1, zf),
grad (bba, xf-1, yf-1, zf),
u);
y1 = lerp(x1, x2, v);
x1 = lerp(grad (aab, xf , yf , zf-1),
grad (bab, xf-1, yf , zf-1),
u);
x2 = lerp(grad (abb, xf , yf-1, zf-1),
grad (bbb, xf-1, yf-1, zf-1),
u);
y2 = lerp (x1, x2, v);
return (lerp (y1, y2, w)+1)/2; //Interpolate everything again and move the range from [-1, 1] to [0, 1]
}
/**
* Layers levels of noise, each with decreasing amplitudes and persistence
* @param x
* @param y
* @param z
* @param octaves
* @param persistence how much each layer impacts the layer below it
* @return
*/
public double octave(double x, double y, double z, int octaves, double persistence) {
double total = 0, frequency = 1, amplitude = 1, maxValue = 0;
for(int i = 0; i < octaves; i++) {
total += perlin(x * frequency, y * frequency, z * frequency) * amplitude;
maxValue += amplitude;
amplitude *= persistence;
frequency *= 2;
}
return total/maxValue;
}
这里是启动方法:
public void enter() {
eOffsetX = r.nextInt(10000);
eOffsetY = r.nextInt(10000); //This will "randomize" the seed of the noise
p = new int[512];
for(int x = 0; x < 512; x++) {
p[x] = permutation[x%256]; //Fill twice
}
eNoise = new float[(int)(1280/tile)][(int)(800/tile)];
gauss = new float[1280];
Arrays.fill(gauss, 0);
for(int i = 0; i < eNoise.length; i++) {
for(int j = 0; j < eNoise[0].length; j++) {
eNoise[i][j] = 1f * 100 * (float) octave(((double)i*zoom+eOffsetX)/1280, ((double)j*zoom+eOffsetY)/800, 0, 7, 0.60);
gauss[(int)(((eNoise[i][j]/100)-.5)*1280*2.5+640)] += 1f;
}
}}
渲染方法:
public void render(Graphics g) {
g.setBackground(Color.white);
g.setColor(Color.black);
for(int k = 0; k < 1280; k++) {
g.fillRect(k, 800-(gauss[k]/10), 5, 5);
}
}
最后,enter() 中提到的排列集:
private static final int[] permutation = { 151,160,137,91,90,15,
131,13,201,95,96,53,194,233,7,225,140,36,103,30,69,142,8,99,37,240,21,10,23,
190, 6,148,247,120,234,75,0,26,197,62,94,252,219,203,117,35,11,32,57,177,33,
88,237,149,56,87,174,20,125,136,171,168, 68,175,74,165,71,134,139,48,27,166,
77,146,158,231,83,111,229,122,60,211,133,230,220,105,92,41,55,46,245,40,244,
102,143,54, 65,25,63,161, 1,216,80,73,209,76,132,187,208, 89,18,169,200,196,
135,130,116,188,159,86,164,100,109,198,173,186, 3,64,52,217,226,250,124,123,
5,202,38,147,118,126,255,82,85,212,207,206,59,227,47,16,58,17,182,189,28,42,
223,183,170,213,119,248,152, 2,44,154,163, 70,221,153,101,155,167, 43,172,9,
129,22,39,253, 19,98,108,110,79,113,224,232,178,185, 112,104,218,246,97,228,
251,34,242,193,238,210,144,12,191,179,162,241, 81,51,145,235,249,14,239,107,
49,192,214, 31,181,199,106,157,184, 84,204,176,115,121,50,45,127, 4,150,254,
138,236,205,93,222,114,67,29,24,72,243,141,128,195,78,66,215,61,156,180
};
这是从0到255的所有数字,顺序随机。
同样,重申一下这个问题,该函数生成了 0.45 到 0.55 之间的大量数字,几乎没有超出该范围的数字。我想重新缩放函数,以便在较低和较高范围内获得更多数字。这可能发生在 perlin()
函数最后一行的某处,但我不确定该怎么做。感谢您的帮助。
我已经通过以下方法解决了这个问题。
首先,在生成噪声值时,我记录了最小值和最大值。
其次,在将噪声值存储到我的数组中时,我对数据进行了标准化: (值 - 最小值)/(最大 - 最小值)。这样,我所有的值都在 0 到 1 之间,但分布得更好。该等式将最小值映射到 (min - min)/(max - min) 或 0,并将最大值映射到 (max - min)/(max - min) 或 1,并缩放介于两者之间的所有值。