Perlin noise的目的是为了产生一系列可重复,伪随机的信号,在游戏中我们可以利用产生的perlin noise来混合地形使用,例如沙地和草地的混合,如下图所示,第三张黑白颜色图就是perlin noise产生的(http://devmag.org.za/2009/04/25/perlin-noise/)。
Perlin noise的产生是从一维扩展到三维的。首先从一维说起,假如我们需要得到一维点x的noise,首先我们取x附近的两个整数点的noise值,然后利用插值函数得到该点的noise。具体计算如下:
//注释掉的为原始的插值函数,非二次连续,高频突变。 float fade(float t) { // return t*t*(3.0-2.0*t); // Old fade, yields discontinuous second derivative return t*t*t*(t*(t*6.0-15.0)+10.0); // Improved fade, yields C2-continuous noise } #define lerp(t, a, b) ( a + t * (b - a) ) float Xdelta = X- (int)X; //X0,X1为最靠近X两个点的噪声值,G为存放各个整数点的噪声值。 float X0 =G[int(x)]; float X1 = G[int(x)+1]; //返回值 float noise = lerp(fade(Xdelta),X0,X1);
二维的noise获取如上图所示,首先获取平面周围四个点的noise值,然后计算四个点的贡献度,最后插值计算出改点的noise(http://webstaff.itn.liu.se/~stegu/simplexnoise/simplexnoise.pdf),代码注释如下。
float noise2(vec2 vec) { //图中的i,j。 vec2 pf = frac(vec);
; //相近的四个点随机梯度值(noise 值) vec2 gradient00 = G2[perm[pf.x][perm[pf.y]]; vec2 gradient01 = G2[perm[pf.x]][perm[pf.y+1]]; vec2 gradient10 = G2[perm[pf.x+1]][perm[pf.y]]; vec2 gradient11 = G2[perm[pf.x+1]][perm[pf.y+1]]; //四个点nosie的贡献值。 float n00 = dot(grad00, vec); float n10 = dot(grad10, Pf - vector2(1.0, 0.0)); float n01 = dot(grad01, Pf - vector2(0.0, 1.0)); float n11 = dot(grad11, Pf - vector2(1.0, 1.0)); //x方向的插值计算。 // Blend contributions along y vec2 n_x = lerp2(vector2(n00, n01), vec2(n10, n11), fade(Pf.x)); //y方向的插值计算。 // Blend contributions along y float n_xy = lerp2(n_x.x, n_x.y, fade(Pf.y)); // We're done, return the final noise value. return n_xy; }
三维perlin noise需要计算空间周围的八个点的nosie值,然后插值获取最终值,计算方式如下。
// Classic Perlin noise, 3D version public static double noise(double x, double y, double z) { // Find unit grid cell containing point //查找最接近的X,Y,Z int X = fastfloor(x); int Y = fastfloor(y); int Z = fastfloor(z); //计算插值。 // Get relative xyz coordinates of point within that cell x = x - X; y = y - Y; z = z - Z; // Wrap the integer cells at 255 (smaller integer period can be introduced here) X = X & 255; Y = Y & 255; Z = Z & 255; //随机索引 // Calculate a set of eight hashed gradient indices int gi000 = perm[X+perm[Y+perm[Z]]] % 12; int gi001 = perm[X+perm[Y+perm[Z+1]]] % 12; int gi010 = perm[X+perm[Y+1+perm[Z]]] % 12; int gi011 = perm[X+perm[Y+1+perm[Z+1]]] % 12; int gi100 = perm[X+1+perm[Y+perm[Z]]] % 12; int gi101 = perm[X+1+perm[Y+perm[Z+1]]] % 12; int gi110 = perm[X+1+perm[Y+1+perm[Z]]] % 12; int gi111 = perm[X+1+perm[Y+1+perm[Z+1]]] % 12; //计算梯度值 // Calculate noise contributions from each of the eight corners double n000= dot(grad3[gi000], x, y, z); double n100= dot(grad3[gi100], x-1, y, z); double n010= dot(grad3[gi010], x, y-1, z); double n110= dot(grad3[gi110], x-1, y-1, z); double n001= dot(grad3[gi001], x, y, z-1); double n101= dot(grad3[gi101], x-1, y, z-1); double n011= dot(grad3[gi011], x, y-1, z-1); double n111= dot(grad3[gi111], x-1, y-1, z-1); // Compute the fade curve value for each of x, y, z double u = fade(x); double v = fade(y); double w = fade(z); // Interpolate along x the contributions from each of the corners //计算x方向插值。 double nx00 = mix(n000, n100, u); double nx01 = mix(n001, n101, u); double nx10 = mix(n010, n110, u); double nx11 = mix(n011, n111, u); //计算y方向插值。 // Interpolate the four results along y double nxy0 = mix(nx00, nx10, v); double nxy1 = mix(nx01, nx11, v); //计算z方向插值。 // Interpolate the two last results along z double nxyz = mix(nxy0, nxy1, w); return nxyz; }