噪聲筆記#3 梯度噪聲_Perlin Noise
value noise 看起來非常“塊狀”。為了消除這種塊狀的效果,在 1985 年 Ken Perlin 開發了另一種 noise 演算法 Gradient Noise。Ken 解決了如何插入隨機的 gradients(梯度、漸變)而不是一個固定值。這些梯度值來自於一個二維的隨機函式,返回一個方向(
vec2
格式的向量),而不僅是一個值(float
格式)。
perlin噪聲相對於之前值噪聲修改了兩個地方 :
1、晶格點上的隨機取值由一維的隨機值換成了二維的隨機向量(也就是所謂的梯度向量)
2、在最後的插值方面,插的四個值(2D)是四個晶格點的梯度向量和距畫素點的距離向量點乘的結果
這就意味著即使是在同一個網格里的畫素點,它們的插得四個值也是不一樣的(值噪聲是一樣的,因為它插得值就是根據晶格點位置得到的偽隨機值,梯度噪聲雖然每個晶格點的梯度向量也是固定的,但是計算時每個畫素點時,它的距離向量各不相同,所以最後得到的插值也不相同。)
PS:Perlin在他的實現中選擇使用蒙特卡洛模擬方法來選取這些隨機梯度向量。隨機梯度向量的選擇是將單位圓分成8份的8個梯度向量(模為1),然後通過一個排列表(permutation),把不同的晶格頂點對應相應的梯度值。這裡為了方便2D直接用隨機取xy值了(範圍在正方形裡,而且不侷限與 8個),如果想了解原來的方法,可以看這個:
float noise(vec2 st) { vec2 i = floor(st); vec2 f = fract(st); vec2 u = f*f*(3.0-2.0*f); return mix( mix( dot( random2(i + vec2(0.0,0.0) ), f - vec2(0.0,0.0) ), dot( random2(i + vec2(1.0,0.0) ), f - vec2(1.0,0.0) ), u.x), mix( dot( random2(i + vec2(0.0,1.0) ), f - vec2(0.0,1.0) ), dot( random2(i + vec2(1.0,1.0) ), f - vec2(1.0,1.0) ), u.x), u.y); }
PS:
2002年,Perlin提出了對插值方式的優化 把插值方式由原來的三次 Hermite函式()換成了四次Hermite函式()
為什麼要換呢?因為四次的函式曲線邊界更加的緩和,晶格與晶格之間的過渡越自然
看上圖,紅色是三次,藍色是四次,在靠近0,1時,四次比三次的變化幅度更小,更加的平緩。那什麼時候值是0,1呢,自然就是位於邊界的時候啦(1d是點,2d是線,3d是面)
2D Perlin噪聲
Shader "Custom/PerlinNoise2D" {
Properties{
_Scale("Scale",Float)=10
}
SubShader{
Pass{
CGPROGRAM
#include "UnityCG.cginc"
#pragma vertex vert
#pragma fragment frag
float _Scale;
struct v2f {
float4 pos:SV_POSITION;
half2 uv:TEXCOORD0;
};
inline float mix(float a, float b, float t) {
return b*t + a*(1 - t);
}
//from:https://www.shadertoy.com/view/XdXGW8
float2 random(float2 x) {
float2 k = float2(0.3183099, 0.3678794);
x = x*k + k.yx;
return -1.0 + 2.0*frac(16.0 * k*frac(x.x*x.y*(x.x + x.y)));
}
float perlinNoise(float2 uv) {
float2 i = floor(uv);
float2 f = frac(uv);
//為了直觀 單獨計算四個值
float value0 = dot(random(i + float2(0, 0)), f - float2(0, 0));
float value1 = dot(random(i + float2(1, 0)), f - float2(1, 0));
float value2 = dot(random(i + float2(0, 1)), f - float2(0, 1));
float value3 = dot(random(i + float2(1, 1)), f - float2(1, 1));
float2 u = f*f*(3.0 - 2.0*f);
//插值
return mix(mix(value0, value1,u.x), mix(value2, value3, u.x), u.y);
}
v2f vert(appdata_base v) {
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.uv = v.texcoord;
return o;
}
fixed4 frag(v2f i) :SV_Target{
//值範圍是(-1,1)要把它對映成(0,1)
float noise = perlinNoise(i.uv*_Scale)*0.5+0.5;
return fixed4(noise,noise,noise, 1);
}
ENDCG
}
}
FallBack "Diffuse"
}
3D Perlin 噪聲
實際上,perlin把三維的梯度向量初始化為到立方體12條邊的向量值,然後隨機取這12個梯度向量,具體可以看這個:https://blog.csdn.net/yolon3000/article/details/77200448
下面的方法仍然是簡化版,直接隨機取值了.
Shader "Custom/PerlinNoise3D" {
Properties{
_Scale("Scale",Float) = 10
}
SubShader{
Pass{
CGPROGRAM
#include "UnityCG.cginc"
#pragma vertex vert
#pragma fragment frag
float _Scale;
struct v2f {
float4 pos:SV_POSITION;
half2 uv:TEXCOORD0;
};
inline float mix(float a, float b, float t) {
return b*t + a*(1 - t);
}
float2 random(float3 x) {
float2 a = float2(dot(x, float3(127.1, 311.7, 74.7)),
dot(x, float3(269.5, 183.3, 246.1)));
return frac(sin(a*43))*2-1;
}
float perlinNoise3D(float3 uv) {
float3 i = floor(uv);
float3 f = frac(uv);
float value0 = dot(random(i + float3(0, 0, 0)), f - float3(0, 0, 0));
float value1 = dot(random(i + float3(1, 0, 0)), f - float3(1, 0, 0));
float value2 = dot(random(i + float3(0, 1, 0)), f - float3(0, 1, 0));
float value3 = dot(random(i + float3(1, 1, 0)), f - float3(1, 1, 0));
float value4 = dot(random(i + float3(0, 0, 1)), f - float3(0, 0, 1));
float value5 = dot(random(i + float3(1, 0, 1)), f - float3(1, 0, 1));
float value6 = dot(random(i + float3(0, 1, 1)), f - float3(0, 1, 1));
float value7 = dot(random(i + float3(1, 1, 1)), f - float3(1, 1, 1));
float3 u = f*f*(3.0 - 2.0*f);
//插值
float mix1= mix(mix(value0, value1,u.x), mix(value2, value3, u.x), u.y);
float mix2= mix(mix(value4, value5, u.x), mix(value6, value7, u.x), u.y);
return mix(mix1,mix2,f.z);
}
v2f vert(appdata_base v) {
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.uv = v.texcoord;
return o;
}
fixed4 frag(v2f i) :SV_Target{
float noise = perlinNoise3D(float3(i.uv*_Scale,_Time.y))*0.5+0.5;
return fixed4(noise,noise,noise, 1);
}
ENDCG
}
}
FallBack "Diffuse"
}