1. 程式人生 > >噪聲筆記#3 梯度噪聲_Perlin Noise

噪聲筆記#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個),如果想了解原來的方法,可以看這個:

https://blog.csdn.net/yolon3000/article/details/77073701


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函式(\small 3x^2-2x^3)換成了四次Hermite函式(\small 6x^5-15x^4+10x^3

為什麼要換呢?因為四次的函式曲線邊界更加的緩和,晶格與晶格之間的過渡越自然 四次hermite函式

看上圖,紅色是三次,藍色是四次,在靠近0,1時,四次比三次的變化幅度更小,更加的平緩。那什麼時候值是0,1呢,自然就是位於邊界的時候啦(1d是點,2d是線,3d是面)


 

2D Perlin噪聲

PerlinNoise2D

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

下面的方法仍然是簡化版,直接隨機取值了.

3dPerlinNoise

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"
}