当前位置: 代码迷 >> 综合 >> 噪声笔记#3 梯度噪声_Perlin Noise
  详细解决方案

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

热度:94   发布时间:2023-12-14 21:34:57.0

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函数()

为什么要换呢?因为四次的函数曲线边界更加的缓和,晶格与晶格之间的过渡越自然 四次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 fragfloat _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/XdXGW8float2 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 fragfloat _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"
}