Perlin Noise Attempt

时间:2016-05-05 04:43:05

标签: c graphics perlin-noise

我正在尝试按照本文档中所述的Perlin Noise(3维)尝试:http://lodev.org/cgtutor/randomnoise.html

然而,这就是我所得到的。 perlin noise attempt 看起来平滑效果不佳。您可以看到'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;
}

1 个答案:

答案 0 :(得分:1)

您的代码有点难以阅读。

对于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)。噪声函数每次都必须为相同的参数返回相同的值。

但是有一个问题:噪音功能只接受整数参数!但我们想以浮点值对其进行采样。

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返回a,如果为1,则返回b。在它之间进行线性插值。

所以将它用于简单的1D噪声采样函数:

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)相同。

要从单个参数noisesample转到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中的结果之间进行插值。我们总共采样噪声()4次。我把它留作练习如何编写noisesample(float x,float y,float z)。它会对noise()进行8次采样并调用Lerp 7次。

所有得到我们的是,我们可以在浮点坐标处采样噪声(有点平滑 - 有更平滑的方式!)。这就是我们制作柏林噪音所需要的!

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; 
}

关键的想法是了解抽样。它只是对简单整数噪声函数进行采样的组合。