引述維基百科的介紹:
Perlin噪聲(Perlin noise,又稱為柏林噪聲)指由Ken Perlin發明的自然噪聲生成演演算法,具有在函數上的連續性,並可在多次呼叫時給出一致的數值。 在電子遊戲領域中可以透過使用Perlin噪聲生成具連續性的地形;或是在藝術領域中使用Perlin噪聲生成圖樣。
維基百科的介紹相當的官方,其實可以理解為一個隨機函數,不過有以下兩個特點:
它適用於希望給定連續的輸入,能夠給出相對連續的隨機輸出。(例如,模擬自然地形生成:想象地形不能前一步是高山,腳下是深谷,後一步又是高山這種連續劇烈的變化)
隨機函數噪聲:
柏林噪聲:
對於有經驗的同學來說,一提到「平滑」,直覺上就會想到插值、平滑函數等。沒錯,柏林噪聲其實就是使用插值、平滑函數,有時會在此基礎上使用倍頻,波形疊加(傅立葉變換)等方法對波形調整。
先把複雜問題簡單化,考慮一個一維的柏林噪聲生成:
上面提到了插值,插值首先要有值:靜態生成一組亂數,在一個座標系中每單位距離散落一個亂數。不妨令:rands是這個亂數陣列,上圖中y1 = rands[0], y2 = rands[1], ...,x2 - x1 = delta_x = 上述的單位距離,建立一個座標系。
對於散落在[0, rands.Len - 1]區間的某個值n來說([rands.Len-1, rands.Len]區間對應的x的點規定不能取到,因為下面計算會推到rands[n + 1]),假設n對應上圖P點則有:
Noise(P) = Y1 + (Y2 - Y1) * F((xp - x1)/(delta_x))
理解下這個公式:
這裡的F是指平滑函數,上述(t)可知F在[0,1]的輸出也必須在[0,1]區間內,通常F(x) = 6 * x^5 - 15 * x^4 - 10 * x^3,顧名思義就是對輸入進行平滑,函數影象如下:
帶入資料來算:
Noise(p) = Y1(xp) + (Y2(xp) - Y1(xp)) * F((xp - x1)/(delta_x))
就不展開了
再來思考下它的實現原理:
其思路可以拓展到2維、3維,以2維舉例:
p落在abcd組成的2維網格中,其實可以視為3次1維的計算:分別計算pab、pcd所在1維直線(ab、cd)的結果,在此基礎上計算pad、pcd所在的線上p點的結果。這個計算會在下面的程式碼實現中更加具象化體現出來。(注意有一點計算是不一樣的,一維中y = kx + b計算兩個點之間的影響在2維空間不適用,點會受到2個維度的影響,具體看下面實現中的範例)
static int p[512] = {
151,160,137,91,90,15,
131,13,201,95,96,53,194,233,7,225,140,36,103,30,69,142,8,99,37,240,21,10,23,
190, 6,148,247,120,234,75,0,26,197,62,94,252,219,203,117,35,11,32,57,177,33,
88,237,149,56,87,174,20,125,136,171,168, 68,175,74,165,71,134,139,48,27,166,
77,146,158,231,83,111,229,122,60,211,133,230,220,105,92,41,55,46,245,40,244,
102,143,54, 65,25,63,161, 1,216,80,73,209,76,132,187,208, 89,18,169,200,196,
135,130,116,188,159,86,164,100,109,198,173,186, 3,64,52,217,226,250,124,123,
5,202,38,147,118,126,255,82,85,212,207,206,59,227,47,16,58,17,182,189,28,42,
223,183,170,213,119,248,152, 2,44,154,163, 70,221,153,101,155,167, 43,172,9,
129,22,39,253, 19,98,108,110,79,113,224,232,178,185, 112,104,218,246,97,228,
251,34,242,193,238,210,144,12,191,179,162,241, 81,51,145,235,249,14,239,107,
49,192,214, 31,181,199,106,157,184, 84,204,176,115,121,50,45,127, 4,150,254,
138,236,205,93,222,114,67,29,24,72,243,141,128,195,78,66,215,61,156,180,
151,160,137,91,90,15,
131,13,201,95,96,53,194,233,7,225,140,36,103,30,69,142,8,99,37,240,21,10,23,
190, 6,148,247,120,234,75,0,26,197,62,94,252,219,203,117,35,11,32,57,177,33,
88,237,149,56,87,174,20,125,136,171,168, 68,175,74,165,71,134,139,48,27,166,
77,146,158,231,83,111,229,122,60,211,133,230,220,105,92,41,55,46,245,40,244,
102,143,54, 65,25,63,161, 1,216,80,73,209,76,132,187,208, 89,18,169,200,196,
135,130,116,188,159,86,164,100,109,198,173,186, 3,64,52,217,226,250,124,123,
5,202,38,147,118,126,255,82,85,212,207,206,59,227,47,16,58,17,182,189,28,42,
223,183,170,213,119,248,152, 2,44,154,163, 70,221,153,101,155,167, 43,172,9,
129,22,39,253, 19,98,108,110,79,113,224,232,178,185, 112,104,218,246,97,228,
251,34,242,193,238,210,144,12,191,179,162,241, 81,51,145,235,249,14,239,107,
49,192,214, 31,181,199,106,157,184, 84,204,176,115,121,50,45,127, 4,150,254,
138,236,205,93,222,114,67,29,24,72,243,141,128,195,78,66,215,61,156,180
};
static float3 grads[12] = {
{1,1,0},
{-1,1,0},
{1,-1,0},
{-1,-1,0},
{1,0,1},
{-1,0,1},
{1,0,-1},
{-1,0,-1},
{0,1,1},
{0,-1,1},
{0,1,-1},
{0,-1,-1}
};
float grad(int hash, float x, float y, float z)
{
float3 v3 = float3(x,y,z);
hash = hash & 0xb;
return dot(grads[hash],v3);
}
int inc(int num) {
num++;
return num;
}
float fade(float t) {
return t * t * t * (t * (t * 6 - 15) + 10);
}
float perlin(float x,float y,float z)
{
int xi = (int)x & 255;
int yi = (int)y & 255;
int zi = (int)z & 255;
float xf = x - xi;
float yf = y - yi;
float zf = z - zi;
float u = fade(xf);
float v = fade(yf);
float w = fade(zf);
int aaa, aba, aab, abb, baa, bba, bab, bbb;
aaa = p[p[p[ xi ]+ yi ]+ zi ];
aba = p[p[p[ xi ]+inc(yi)]+ zi ];
aab = p[p[p[ xi ]+ yi ]+inc(zi)];
abb = p[p[p[ xi ]+inc(yi)]+inc(zi)];
baa = p[p[p[inc(xi)]+ yi ]+ zi ];
bba = p[p[p[inc(xi)]+inc(yi)]+ zi ];
bab = p[p[p[inc(xi)]+ yi ]+inc(zi)];
bbb = p[p[p[inc(xi)]+inc(yi)]+inc(zi)];
float x1, x2, y1, y2;
x1 = lerp( grad (aaa, xf , yf , zf),
grad (baa, xf-1, yf , zf),
u);
x2 = lerp( grad (aba, xf , yf-1, zf),
grad (bba, xf-1, yf-1, zf),
u);
y1 = lerp(x1, x2, v);
x1 = lerp( grad (aab, xf , yf , zf-1),
grad (bab, xf-1, yf , zf-1),
u);
x2 = lerp( grad (abb, xf , yf-1, zf-1),
grad (bbb, xf-1, yf-1, zf-1),
u);
y2 = lerp (x1, x2, v);
return lerp (y1, y2, w);
}
這段程式碼是3維的perlin函數,控制引數也可以實現1維、2維計算,從perlin函數看起:
float rand(float2 p){
return frac(sin(dot(p ,float2(12.9898,78.233))) * 43758.5453);
}
float noise(float2 x)
{
float2 i = floor(x);
float2 f = frac(x);
float a = rand(i);
float b = rand(i + float2(1.0, 0.0));
float c = rand(i + float2(0.0, 1.0));
float d = rand(i + float2(1.0, 1.0));
float2 u = f * f * f * (f * (f * 6 - 15) + 10);
float x1 = lerp(a,b,u.x);
float x2 = lerp(c,d,u.x);
return lerp(x1,x2,u.y);
}
可以看到這種實現和上文中的思路是一樣的,只是hash函數和計算各個方向上的影響計算進行了簡化。
可以看出柏林函數的輸出具有「波」的特點,那麼自然可以所有對於波的操作。
進行類似正弦波調幅、調頻、調相,還可以上下偏移
(f(x)=Asin(ωx+φ) + b 這裡 A = 0.5, w = 2, φ = 1, b = 0.5)
波的疊加
傅立葉變換說一個波可以由為n個波疊加而成,疊加結果如圖所示。
波形的調整在實際應用中作用很大,如: