柏林噪音尝试
Perlin Noise Attempt
我正在尝试使用本文档中概述的 Perlin 噪声(3 维):http://lodev.org/cgtutor/randomnoise.html
但是,这就是我得到的。
看起来平滑不起作用。您可以看到 'size' 参数大小的块。有人可以指出我做错了什么吗?
这是我的代码:
%ffp
ctl(1):standard,"Size",range=(1,256), pos=(300,20), size=(120,*),val=64,track, action=preview
onFilterStart:
{
allocArray(9,64,64,64,4); // Array for noise depth
for(int z = 0; z < 64; z++)
for(int y = 0; y < 64; y++)
for(int x = 0; x < 64; x++) {
fputArray(9,x,y,z,(float)(rand() % 32768) / 32768.0);
}
return false;
}
forEveryTile:
{
double fractX,fractY,fractZ,xx,yy,zz;
int x1,y1,z1,x2,y2,z2,col;
double value = 0.0, value2 = 0.0, size, isize=(float)ctl(1);
// int X=screen Width, int Y=screen Height
for(int y = 0; y < Y; y++) {
for(int x = 0; x < X; x++) {
//for(int z = 0; z < 64; z++) {
value2 = 0.0;
size = isize;
while (size >=1.0) {
xx=(float)x/size;
yy=(float)y/size;
zz=(float)clock()/size;
fractX = xx - (int)(xx);
fractY = yy - (int)(yy);
fractZ = zz - (int)(zz);
x1 = ((int)(xx) + 64) % 64;
y1 = ((int)(yy) + 64) % 64;
z1 = ((int)(zz) + 64) % 64;
x2 = (x1 + 64- 1) % 64;
y2 = (y1 + 64- 1) % 64;
z2 = (z1 + 64- 1) % 64;
value=0.0;
value += fractX * fractY * fractZ * fgetArray(9,z1,y1,x1);
value += fractX * (1 - fractY) * fractZ * fgetArray(9,z1,y2,x1);
value += (1 - fractX) * fractY * fractZ * fgetArray(9,z1,y1,x2);
value += (1 - fractX) * (1 - fractY) * fractZ * fgetArray(9,z1,y2,x2);
value += fractX * fractY * (1 - fractZ) * fgetArray(9,z2,y1,x1);
value += fractX * (1 - fractY) * (1 - fractZ) * fgetArray(9,z2,y2,x1);
value += (1 - fractX) * fractY * (1 - fractZ) * fgetArray(9,z2,y1,x2);
value += (1 - fractX) * (1 - fractY) * (1 - fractZ) * fgetArray(9,z2,y2,x2);
value2 += value*size;
size /= 2.0;
}
col=(int)((float)(128.0 * value2 / isize));
col=max(min(col,255),0);
psetp(x,y,RGB(col,col,col));
//} //z
} //x
} //y
return true;
}
你的代码写起来有点难读。
对于 Perlin 噪声,从整数噪声函数开始,其行为类似于哈希。
float noise(int x, int y, int z) { return hash(x+y*5+z*7); }
或
float noise(int x, int y, int z) { return array[x%w+y%h*w+z%d*w*h]; }
这些只是例子。重要的部分是噪音(x,y,z)=噪音(x,y,z)。噪声函数每次都必须return相同参数的相同值。
但是有一个问题:噪声函数只接受整数参数!但我们想以浮点值对其进行采样。
float noisesample (float x, float y, float z) { ... }
最简单的方法是使用线性过滤。任何正浮点值都在 (int)pos 和 ((int)pos)+1 之间。在子位置 pos-(int)pos。这让我们:
float Lerp(float a, float b, float f) { return a+(b-a)*f; }
其中f是[0..1]范围内的子位置,a,b是左边和右边的值。如果f为0,Lerp returns a,如果为1,则returns b。在两者之间进行线性插值。
因此将其用于简单的一维噪声采样函数:
float noisesample(float x) { return Lerp(noise((int)x), noise((int)x+1), fract(x) }
和
float fract(float x) { return x-(int)x; }
我在这里自由地使用 (int)x,如果 x 是正数,它与 floor(x) 相同。
从单个参数噪声样本到 x,y 很容易:在 y 和 y+1 处对 x 进行两次 Lerp,并在它们之间进行 Lerp:
float noisesample(float x, float y) {
float y0 = Lerp(noise((int)x,(int)y), noise((int)x+1,(int)y), fract(x) }
float y1 = Lerp(noise((int)x,(int)y+1), noise((int)x+1,(int)y+1), fract(x) }
return Lerp ( y0, y1, fract(y) );
}
首先对 x 进行两次插值,然后对 y 的结果进行插值。我们总共对 noise() 进行了 4 次采样。我将其作为练习如何编写噪声样本(float x、float y、float z)。它将对 noise() 进行八次采样并调用 Lerp 七次。
让我们得到的只是我们可以在浮动坐标处采样噪声(有点平滑 - 有更平滑的方法!)。这就是我们制作柏林噪音所需要的!
float perlin(float x, float y, float z, int oc=4) {
// maybe: x = x*2^oc, y, z...
float r = 0;
float s = 1;
for ( int i=0; i<oc; i++ ) {
r += noisesample(x,y,z) * s;
s/=2.0f; // to taste
x/=2.0f;
y/=2.0f;
z/=2.0f;
}
return r;
}
关键思想是理解抽样。它只是一个简单的整数噪声函数采样的组合。
我正在尝试使用本文档中概述的 Perlin 噪声(3 维):http://lodev.org/cgtutor/randomnoise.html
但是,这就是我得到的。
这是我的代码:
%ffp
ctl(1):standard,"Size",range=(1,256), pos=(300,20), size=(120,*),val=64,track, action=preview
onFilterStart:
{
allocArray(9,64,64,64,4); // Array for noise depth
for(int z = 0; z < 64; z++)
for(int y = 0; y < 64; y++)
for(int x = 0; x < 64; x++) {
fputArray(9,x,y,z,(float)(rand() % 32768) / 32768.0);
}
return false;
}
forEveryTile:
{
double fractX,fractY,fractZ,xx,yy,zz;
int x1,y1,z1,x2,y2,z2,col;
double value = 0.0, value2 = 0.0, size, isize=(float)ctl(1);
// int X=screen Width, int Y=screen Height
for(int y = 0; y < Y; y++) {
for(int x = 0; x < X; x++) {
//for(int z = 0; z < 64; z++) {
value2 = 0.0;
size = isize;
while (size >=1.0) {
xx=(float)x/size;
yy=(float)y/size;
zz=(float)clock()/size;
fractX = xx - (int)(xx);
fractY = yy - (int)(yy);
fractZ = zz - (int)(zz);
x1 = ((int)(xx) + 64) % 64;
y1 = ((int)(yy) + 64) % 64;
z1 = ((int)(zz) + 64) % 64;
x2 = (x1 + 64- 1) % 64;
y2 = (y1 + 64- 1) % 64;
z2 = (z1 + 64- 1) % 64;
value=0.0;
value += fractX * fractY * fractZ * fgetArray(9,z1,y1,x1);
value += fractX * (1 - fractY) * fractZ * fgetArray(9,z1,y2,x1);
value += (1 - fractX) * fractY * fractZ * fgetArray(9,z1,y1,x2);
value += (1 - fractX) * (1 - fractY) * fractZ * fgetArray(9,z1,y2,x2);
value += fractX * fractY * (1 - fractZ) * fgetArray(9,z2,y1,x1);
value += fractX * (1 - fractY) * (1 - fractZ) * fgetArray(9,z2,y2,x1);
value += (1 - fractX) * fractY * (1 - fractZ) * fgetArray(9,z2,y1,x2);
value += (1 - fractX) * (1 - fractY) * (1 - fractZ) * fgetArray(9,z2,y2,x2);
value2 += value*size;
size /= 2.0;
}
col=(int)((float)(128.0 * value2 / isize));
col=max(min(col,255),0);
psetp(x,y,RGB(col,col,col));
//} //z
} //x
} //y
return true;
}
你的代码写起来有点难读。
对于 Perlin 噪声,从整数噪声函数开始,其行为类似于哈希。
float noise(int x, int y, int z) { return hash(x+y*5+z*7); }
或
float noise(int x, int y, int z) { return array[x%w+y%h*w+z%d*w*h]; }
这些只是例子。重要的部分是噪音(x,y,z)=噪音(x,y,z)。噪声函数每次都必须return相同参数的相同值。
但是有一个问题:噪声函数只接受整数参数!但我们想以浮点值对其进行采样。
float noisesample (float x, float y, float z) { ... }
最简单的方法是使用线性过滤。任何正浮点值都在 (int)pos 和 ((int)pos)+1 之间。在子位置 pos-(int)pos。这让我们:
float Lerp(float a, float b, float f) { return a+(b-a)*f; }
其中f是[0..1]范围内的子位置,a,b是左边和右边的值。如果f为0,Lerp returns a,如果为1,则returns b。在两者之间进行线性插值。
因此将其用于简单的一维噪声采样函数:
float noisesample(float x) { return Lerp(noise((int)x), noise((int)x+1), fract(x) }
和
float fract(float x) { return x-(int)x; }
我在这里自由地使用 (int)x,如果 x 是正数,它与 floor(x) 相同。
从单个参数噪声样本到 x,y 很容易:在 y 和 y+1 处对 x 进行两次 Lerp,并在它们之间进行 Lerp:
float noisesample(float x, float y) {
float y0 = Lerp(noise((int)x,(int)y), noise((int)x+1,(int)y), fract(x) }
float y1 = Lerp(noise((int)x,(int)y+1), noise((int)x+1,(int)y+1), fract(x) }
return Lerp ( y0, y1, fract(y) );
}
首先对 x 进行两次插值,然后对 y 的结果进行插值。我们总共对 noise() 进行了 4 次采样。我将其作为练习如何编写噪声样本(float x、float y、float z)。它将对 noise() 进行八次采样并调用 Lerp 七次。
让我们得到的只是我们可以在浮动坐标处采样噪声(有点平滑 - 有更平滑的方法!)。这就是我们制作柏林噪音所需要的!
float perlin(float x, float y, float z, int oc=4) {
// maybe: x = x*2^oc, y, z...
float r = 0;
float s = 1;
for ( int i=0; i<oc; i++ ) {
r += noisesample(x,y,z) * s;
s/=2.0f; // to taste
x/=2.0f;
y/=2.0f;
z/=2.0f;
}
return r;
}
关键思想是理解抽样。它只是一个简单的整数噪声函数采样的组合。