1. 程式人生 > >噪聲筆記#6 分形布朗運動

噪聲筆記#6 分形布朗運動

分形布朗運動(Fractal Brownian Motion)也就是fbm,它不是噪聲,但是他可以讓噪聲有更多的細節。可以看成把不同比例位置的一張噪聲合併在一起。

// Properties
const int octaves = 1;
float lacunarity = 2.0;
float gain = 0.5;
//
// Initial values
float amplitude = 0.5;
float frequency = 1.;
//
// Loop of octaves
for (int i = 0; i < octaves; i++) {
	value += amplitude * noise(frequency*uv);
	frequency *= lacunarity;
	amplitude *= gain;
}

這就是一種 fbm函式  前面講了可以吧fbm看成多張噪聲的疊加

amplitude表示每一次噪聲疊加的權值,frequency 則是疊加噪聲的比例 

octaves是迴圈次數,可以看成疊加幾張噪聲

lacunarity和gain則是用於修改amplitude和frequency 的值,讓每次疊加的噪聲權值和大小都不同。

fbm噪聲

值噪聲+fbm

Shader "Custom/FBMValueNoise" {
	Properties{
		_Scale("Scale",Range(4,20)) = 10
		[Header(properties)]
		_Octaves("Octaves",Int) = 1
		_Lacunarity("lacunarity",Float)=2
		_Gain("gain",Float)=0.5
		[Header(fbm init)]
		_Amplitude("amolitude",Float) = 0.5
		_Frequency("frequency",Float) = 1.0
	}
	SubShader{
		Pass{
		CGPROGRAM
		#include "UnityCG.cginc"
		#pragma vertex vert
		#pragma fragment frag
		float _Scale;
		int _Octaves;
		float _Lacunarity;
		float _Gain;
		float _Amplitude;
		float _Frequency;
		struct v2f {
			float4 pos:SV_POSITION;
			half2 uv:TEXCOORD0;
		};
		v2f vert(appdata_base v) {
			v2f o;
			o.pos = UnityObjectToClipPos(v.vertex);
			o.uv = v.texcoord;
			return o;
		}
		float rand(float2 st) {
			return frac(sin(dot(st.xy,
				float2(12.9898, 78.233)))
				* 43758.5453123);
		}
		float mix(float a, float b, float t) {
			return b*t + a*(1 - t);
		}
		float ValueNoise(float2 uv) {
			float2 i = floor(uv);
			float2 f = frac(uv);
			float a = rand(i);
			float b = rand(i + float2(1, 0));
			float c = rand(i + float2(0, 1));
			float d = rand(i + float2(1, 1));
			float2 u = f*f*(3.0 - 2.0*f);
			return mix(a, b, u.x) +
				(c - a)* u.y * (1.0 - u.x) +
				(d - b) * u.x * u.y;
			//return mix(mix(a, b, u.x), mix(c, d, u.x), u.y);
		}
		float fbm(float2 uv) {
            /*如果要多次使用,要把_Frequency和_Amplitude賦值給新的,不能直接用,因為這兩個是全域性的*/
			float v;
			for (int i = 0; i < _Octaves; i++) {
				v += _Amplitude*ValueNoise(_Frequency*uv);
				_Frequency *= _Lacunarity;
				_Amplitude *= _Gain;
			}
			return v;
		}
		fixed4 frag(v2f i) :SV_Target{
			half2 uv = i.uv * _Scale;
			float noise = fbm(uv);
			return fixed4(noise, noise, noise, 1);
		}
		ENDCG
	}
	}
		FallBack "Diffuse"
}

perlin噪聲+fbm

simplex噪聲+fbm

其他的FBM函式

上面其中一種FBM方法,你還可以用其他的FBM,會有不同的效果 

湍流

for (int i = 0; i < OCTAVES; i++) {
    value += amplitude * abs(snoise(st));
    st *= 2.;
    amplitude *= .5;
}

這裡多了個絕對值的運算

山脊

   n = abs(n);     // create creases
    n = offset - n; // invert so creases are at top
    n = n * n;      // sharpen creases


PS:湍流山脊這兩張圖和我參考的文章生成的圖差別很大,可能是生成噪聲的方式不同,建議參考原文


域翹曲(Domain Warping)

我們還可以用噪聲來扭曲紋理座標,可以看Inigo Quiles的這篇文章

Shader "Custom/Domain Warping" {
	Properties{
		_Scale("Scale",Float) = 10
		[Header(properties)]
		_Octaves("Octaves",Int) = 1
		_Lacunarity("lacunarity",Float) = 2
		_Gain("gain",Float) = 0.5
		[Header(fbm init)]
		_Amplitude("amolitude",Float) = 0.5
		_Frequency("frequency",Float) = 1.0
		[Header(Color)]
		_Color0("Color0",Color)= (0.101961, 0.619608, 0.666667)
		_Color1("Color1",Color) = (0.666667, 0.666667, 0.498039)
		_Color2("Color2",Color) = (0, 0, 0.164706)
		_Color3("Color3",Color) = (0.666667, 1, 1)
	}
	SubShader{
		Pass{
			CGPROGRAM
			#include "UnityCG.cginc"
			#pragma vertex vert
			#pragma fragment frag
			float _Scale;
			int _Octaves;
			float _Lacunarity;
			float _Gain;
			float _Amplitude;
			float _Frequency;
			float3 _Color0;
			float3 _Color1;
			float3 _Color2;
			float3 _Color3;
			struct v2f {
				float4 pos:SV_POSITION;
				half2 uv:TEXCOORD0;
			};
			inline float mix(float a, float b, float t) {
				return b*t + a*(1 - t);
			}
			inline float3 mix3(float3 a, float3 b, float t) {
				return float3(mix(a.x, b.x, t), mix(a.y, b.y, t), mix(a.z, b.z, 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);
			}
			float fbm(in float2 uv) {
				float v;
				float frequency = _Frequency;
				float amplitude = _Amplitude;
				for (int i = 0; i < _Octaves; i++) {
					v += amplitude*perlinNoise(frequency*uv);
					frequency *= _Lacunarity;
					amplitude *= _Gain;
				}
				return v;
			}
			float3 dw(float2 uv) {
				
				//新增的向量只是單純的偏移可以隨便改
				//第一層扭曲
				float2 q = float2(fbm(uv + float2(0.0, 0.0)),
					fbm(uv + float2(5.2, 1.3)));
				//第二層扭曲
				float2 r = float2(fbm(uv + 4.0*q + float2(1.7, 9.2) + 0.23*_Time.y),
					fbm(uv + 4.0*q + float2(8.3, 2.8) + 0.53*_Time.y));
				//第三層扭曲
				float f= fbm(uv+4*r);

				//上色
				//第三層
				float3 color = mix3(_Color0,_Color1,
					clamp((f*f)*4.0, 0.0, 1.0));
				//第一層
				color = mix3(color, _Color2,
					clamp(length(q), 0.0, 1.0));
				//第二層
				color = mix3(color, _Color3,
					clamp(length(r.x), 0.0, 1.0));
					
				return color;
			}
			v2f vert(appdata_base v) {
				v2f o;
				o.pos = UnityObjectToClipPos(v.vertex);
				o.uv = v.texcoord;
				return o;
			}
			fixed4 frag(v2f i) :SV_Target{
				float3 color = dw(i.uv*_Scale);
				return fixed4(color, 1);
			}
			ENDCG
			}
	}
		FallBack "Diffuse"
}

參考內容:https://thebookofshaders.com/13/?lan=ch