柏林噪声由Ken Perlin发明,很多人使用柏林噪音来获得一个比较酷的纹理效果。 Perlin纹理不会像这样返回白噪声: [

](http://www.wjgbaby.com/wp-content/uploads/2018/07/18070201.jpg)](http://www.wjgbaby.com/wp-content/uploads/2018/07/18070201.jpg) 相反,它返回类似于模糊白噪声的东西: [![](http://www.wjgbaby.com/wp-content/uploads/2018/07/18070202.jpg)
](http://www.wjgbaby.com/wp-content/uploads/2018/07/18070201.jpg)](http://www.wjgbaby.com/wp-content/uploads/2018/07/18070201.jpg) 相反,它返回类似于模糊白噪声的东西: [![](http://www.wjgbaby.com/wp-content/uploads/2018/07/18070202.jpg)

除了简单迅速以外,柏林噪声的另一个关键特性是它对于相同的输入永远返回相同的随机数字,输入点附近的点返回近似的数字。

class perlin {
public:
float noise(const vec3& p) const {
float u = p.x() - floor(p.x());
float v = p.y() - floor(p.y());
float w = p.z() - floor(p.z());
int i = int(4 * p.x()) & 255;
int j = int(4 * p.y()) & 255;
int k = int(4 * p.z()) & 255;
return ranfloat[perm_x[i] ^ perm_y[j] ^ perm_z[k]];
}
static float *ranfloat;
static int *perm_x;
static int *perm_y;
static int *perm_z;
};

static float* perlin_generate() {
float * p = new float[256];
for (int i = 0; i < 256; ++i)
p[i] = (rand() % (100) / (float)(100));
return p;
}

void permute(int *p, int n) {
for (int i = n-1; i > 0; i–) {
int target = int((rand() % (100) / (float)(100))*(i+1));
int tmp = p[i];
p[i] = p[target];
p[target] = tmp;
}
return;
}

static int* perlin_generate_perm() {
int * p = new int[256];
for (int i = 0; i < 256; i++)
p[i] = i;
permute(p, 256);
return p;
}

float *perlin::ranfloat = perlin_generate();
int *perlin::perm_x = perlin_generate_perm();
int *perlin::perm_y = perlin_generate_perm();
int *perlin::perm_z = perlin_generate_perm();

生成噪声纹理:

class noise_texture : public texture {
public:
noise_texture() {}
virtual vec3 value(float u, float v, const vec3& p) const {
return vec3(1, 1, 1)*noise.noise(p);
}
perlin noise;
};

使用两个sphere表现噪声纹理:

hitable *RandomScene() {
texture *pertext = new noise_texture();
hitable **list = new hitable*[2];
list[0] = new sphere(vec3(0, -1000, 0), 1000, new lambertian(pertext));
list[1] = new sphere(vec3(0, 2, 0), 2, new lambertian(pertext));

return new hitable\_list(list, 2);

}

效果如下: [{% image http://www.wjgbaby.com/wp-content/uploads/2018/07/18070203.jpg '](http://www.wjgbaby.com/wp-content/uploads/2018/07/18070203.jpg)' '' %} 为了使其平滑,我们可以线性插值:

inline float perlin_interp(float c[2][2][2], float u, float v, float w) {
float accum = 0;
for (int i=0; i < 2; i++)
for (int j=0; j < 2; j++)
for (int k=0; k < 2; k++)
accum +=(i*u + (1 - i)*(1 - u))*
(j*v + (1 - j)*(1 - v))*
(k*w + (1 - k)*(1 - w))*c[i][j][k];

return accum;

}

class perlin {
public:
float noise(const vec3& p) const {
float u = p.x() - floor(p.x());
float v = p.y() - floor(p.y());
float w = p.z() - floor(p.z());
int i = floor(p.x());
int j = floor(p.y());
int k = floor(p.z());

    float c\[2\]\[2\]\[2\];
    for (int di=0; di < 2; di++)
    for (int dj=0; dj < 2; dj++)
        for (int dk=0; dk < 2; dk++)
            c\[di\]\[dj\]\[dk\] = ranfloat\[perm\_x\[(i+di) & 255\] ^ perm\_y\[(j+dj) & 255\] ^ perm\_z\[(k+dk) & 255\]\];
    return perlin\_interp(c, u, v, w);
}
    static float \*ranfloat;
    static int \*perm\_x;
    static int \*perm\_y;
    static int \*perm\_z;

};

效果如下: [

](http://www.wjgbaby.com/wp-content/uploads/2018/07/18070204.jpg)
](http://www.wjgbaby.com/wp-content/uploads/2018/07/18070204.jpg)
使用hermite立方体来舍入插值,使图像更平滑:

class perlin {
public:
float noise(const vec3& p) const {
float u = p.x() - floor(p.x());
float v = p.y() - floor(p.y());
float w = p.z() - floor(p.z());
u = u*u*(3 - 2 * u);
v = v*v*(3 - 2 * v);
w = w*w*(3 - 2 * w);

    int i = floor(p.x());
    int j = floor(p.y());
    int k = floor(p.z());

    float c\[2\]\[2\]\[2\];
    for (int di=0; di < 2; di++)
    for (int dj=0; dj < 2; dj++)
        for (int dk=0; dk < 2; dk++)
            c\[di\]\[dj\]\[dk\] = ranfloat\[perm\_x\[(i+di) & 255\] ^ perm\_y\[(j+dj) & 255\] ^ perm\_z\[(k+dk) & 255\]\];
    return perlin\_interp(c, u, v, w);
}
    static float \*ranfloat;
    static int \*perm\_x;
    static int \*perm\_y;
    static int \*perm\_z;

};

效果如下: [

](http://www.wjgbaby.com/wp-content/uploads/2018/07/18070205.jpg)
](http://www.wjgbaby.com/wp-content/uploads/2018/07/18070205.jpg)
缩放输入点使其变化更快:

class noise_texture : public texture {
public:
noise_texture() {}
noise_texture(float sc) : scale(sc) {}
virtual vec3 value(float u, float v, const vec3& p) const {
//return vec3(1, 1, 1)*noise.noise(p);
return vec3(1, 1, 1)*noise.noise(scale * p);
}
perlin noise;
float scale;
};

hitable *RandomScene() {
texture *pertext = new noise_texture(3);
hitable **list = new hitable*[2];
list[0] = new sphere(vec3(0, -1000, 0), 1000, new lambertian(pertext));
list[1] = new sphere(vec3(0, 2, 0), 2, new lambertian(pertext));

return new hitable\_list(list, 2);

}

效果如下: [{% image http://www.wjgbaby.com/wp-content/uploads/2018/07/18070206.jpg '](http://www.wjgbaby.com/wp-content/uploads/2018/07/18070206.jpg)' '' %} 仍然能看到网格,可能是因为该模式的最小值和最大值总是完全落在整数x / y / z上。 肯佩林非常聪明的把戏是在格点上放置随机单位矢量(而不是浮点数),并使用点积来移动格子上的最小和最大值。 所以,首先我们需要将随机浮点数更改为随机向量:

#ifndef PERLINH
#define PERLINH

#include “vec3.h”

inline float perlin_interp(vec3 c[2][2][2], float u, float v, float w) {
float uu = u*u*(3-2*u);
float vv = v*v*(3-2*v);
float ww = w*w*(3-2*w);
float accum = 0;

for (int i=0; i < 2; i++)
    for (int j=0; j < 2; j++)
        for (int k=0; k < 2; k++) {
    vec3 weight\_v(u-i, v-j, w-k);
    accum += (i\*uu + (1-i)\*(1-uu))\*
         (j\*vv + (1-j)\*(1-vv))\*
         (k\*ww + (1-k)\*(1-ww))\*dot(c\[i\]\[j\]\[k\], weight\_v);
    }
return accum;

}

class perlin {
public:
float noise(const vec3& p) const {
float u = p.x() - floor(p.x());
float v = p.y() - floor(p.y());
float w = p.z() - floor(p.z());
int i = floor(p.x());
int j = floor(p.y());
int k = floor(p.z());

    vec3 c\[2\]\[2\]\[2\];
    for (int di=0; di < 2; di++)
        for (int dj=0; dj < 2; dj++)
        for (int dk=0; dk < 2; dk++)
            c\[di\]\[dj\]\[dk\] = ranvec\[perm\_x\[(i+di) & 255\] ^ perm\_y\[(j+dj) & 255\] ^ perm\_z\[(k+dk) & 255\]\];
    return perlin\_interp(c, u, v, w);
}
static vec3 \*ranvec;
static int \*perm\_x;
static int \*perm\_y;
static int \*perm\_z;

};

static vec3* perlin_generate() {
vec3 * p = new vec3[256];
for ( int i = 0; i < 256; ++i )
p[i] = unit_vector(vec3(-1 + 2*(rand() % (100) / (float)(100)),
-1 + 2*(rand() % (100) / (float)(100)),
-1 + 2*(rand() % (100) / (float)(100))));
return p;
}

void permute(int *p, int n) {
for (int i = n-1; i > 0; i–) {
int target = int((rand() % (100) / (float)(100))*(i+1));
int tmp = p[i];
p[i] = p[target];
p[target] = tmp;
}
return;
}

static int* perlin_generate_perm() {
int * p = new int[256];
for (int i = 0; i < 256; i++)
p[i] = i;
permute(p, 256);
return p;
}

vec3 *perlin::ranvec = perlin_generate();
int *perlin::perm_x = perlin_generate_perm();
int *perlin::perm_y = perlin_generate_perm();
int *perlin::perm_z = perlin_generate_perm();

#endif

效果如下: [

](http://www.wjgbaby.com/wp-content/uploads/2018/07/18070207.jpg)
](http://www.wjgbaby.com/wp-content/uploads/2018/07/18070207.jpg)
通常使用具有多个相加频率的复合噪声。 这通常称为混乱,并且是重复调用的噪音总和:

float turb(const vec3& p, int depth=7) const {
float accum = 0;
vec3 temp_p = p;
float weight = 1.0;
for (int i = 0; i < depth; i++) {
accum += weight*noise(temp_p);
weight *= 0.5;
temp_p *= 2;
}
return fabs(accum);
}

这里fabs()是math.h绝对值函数。 效果如下: [

](http://www.wjgbaby.com/wp-content/uploads/2018/07/18070208.jpg)
](http://www.wjgbaby.com/wp-content/uploads/2018/07/18070208.jpg)
做一个类似大理石纹理的效果:

class noise_texture : public texture {
public:
noise_texture() {}
noise_texture(float sc) : scale(sc) {}
virtual vec3 value(float u, float v, const vec3& p) const {
return vec3(1,1,1)*noise.turb(scale * p);
}
perlin noise;
float scale;
};

效果如下: 参考书籍:《Ray Tracing The Next Week》 RTTNW系列项目地址:GitHub