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函数()换成了四次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"
}