噪声笔记#3 梯度噪声_Perlin Noise

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_38275140/article/details/84197364

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

猜你喜欢

转载自blog.csdn.net/qq_38275140/article/details/84197364