任程的博客

计算机图形、动画、虚拟现实 / 3D Computer Graphics, Computer Animation, Virtual Reality / uStep禹步信息科技
  首页  :: 联系 :: 订阅 订阅  :: 管理

Perlin噪声

Posted on 2008-02-21 21:18  任程  阅读(3600)  评论(1编辑  收藏  举报

本文资源来自libnoise网站和GPU Gems一书。

一.什么是Coherent noise

Coherent noise由coherent noise函数生成,具备三个特点:

1)        相同的输入总得到相同的输出

2)        输入的小变化引起输出的小变化

3)        输入的大幅变化引起输出的随机变化


coherent-noise函数输出


non-coherent-noise函数输出

如何生成Coherent noise将会在第四部分给出。

二.什么是Perlin Noise

Perlin Noise是几个Coherent noise的叠加,这几个coherent noise间的关系是:频率递增,振幅递减。

下图中为f为频率,a为振幅。


以上几个噪声的和就组成了Perlin噪声:


Ken Perlin1983年发明Perlin噪声,1985年发表相应文章。

三.Perlin噪声中的各个术语

1.         Octave

由多个coherent noise组成一个Perlin噪声,每一个coherent noise就称为一个octave.

octave越多,Perlin噪声就越细致,但计算时间也越长。

2.         振幅Amplitude

每个Coherent noise函数所能输出的最大值,在有名的libnoise库中振幅为n的函数输出范围为-n~n

3.         频率Frequency

单位长度内Coherent noise函数循环的次数,每次循环的起点函数输出为0

4.         Lacunarity

两个连续octave间频率frequency递增的速度的度量。

当前octave的频率f * lacunarity = 下一个octave的频率f1

5.         Persistence

两个连续octave间振幅amplitude递增的速度的度量。

当前octave的振幅A * Persistence = 下一个octave的振幅A1

6.         Seed

用于改变Coherent noise函数的输出。改变Seed值,则Coherent noise函数的输出改变。

四.如何生成Coherent noise

Perlin噪声由Coherent noise叠加生成,那么只要知道了如何生成Coherent noise ,就能生成Perlin噪声了。下面将逐步说明。

1.         Integer-noise函数

给定一个整数作为输入,则一维Integer-noise函数输出一个伪随机浮点数(通常在-11之间)。NInteger-noise函数需要n个整数作为输入,输出仍是一个伪随机浮点数(通常在-1-1之间)。


下面是一个这样的函数:

double IntegerNoise (int n)

{

 n = (n >> 13) ^ n;

 int nn = (n * (n * n * 60493 + 19990303) + 1376312589) & 0x7fffffff;

 return 1.0 - ((double)nn / 1073741824.0);

}

2.         Interger-noise间进行插值

线形插值linear interpolation: lerp(a, b, w) = a * (1 - w) + b * w权重值w01之间。

函数如下:

double CoherentNoise (double x)

{

 int intX = (int)(floor (x));

 double n0 = IntegerNoise (intX);

 double n1 = IntegerNoise (intX + 1);

 double weight = x - floor (x);

 double noise = lerp (n0, n1, weight);

 return noise;

}

二维函数用双线性插值bilinear interpolation,三维函数用三线性插值trilinear interpolation

下图是一维插值后的输出:


二维输出:


线性插值的缺点在于生成的纹理有creasing瑕疵,也就是说界线太明显,图像不自然。在生成凹凸纹理时尤其明显,如下图:


3.         在线性插值的基础上进一步改进

线性插值的方法保证了插值函数的输出在插值点上取得了正确的结果,但不能保证插值函数在插值点上的导数值。

利用Hermit插值可以在保证函数输出的基础上保证插值函数的导数在插值点上为0,这样就提供了平滑性。

插值点为01,结果值为01,导数为00,则可以求得Hermit插值函数为s(x) = -2x3 + 3x2 也就是所谓的S曲线函数。

用立方插值(S曲线插值)代替线性插之后的代码为:

double CoherentNoise (double x)

{

 int intX = (int)(floor (x));

 double n0 = IntegerNoise (intX);

 double n1 = IntegerNoise (intX + 1);

 double weight = x - floor (x);

 // double noise = lerp (n0, n1, weight);

 double noise = lerp (n0, n1, SCurve (weight));

 return noise;

}

如果再使插值函数的二阶导数值在插值点处为0,则可以进一步平滑。五次插值函数:s(x) = 6x5 - 15x4 + 10x3

      

           五次插值                              三次插值                                线性插值


ps:libnoise开源库可以生成Perlin noise。