【Shader】fBm（非整数ブラウン運動）を使ったシェーダお遊び

#はじめに
本エントリは[The Book of Shaders: Fractal Brownian Motion](https://thebookofshaders.com/13/?lan=jp)の内容をUnityで実装して遊んだみたという内容です
The Book of ShadersはGLSLで記述されていますが、Unityでシェーダを使ったお遊びにも非常に有益な内容が多くありますので、シェーダに興味のある人は是非内容をさらってみるといいと思います

#fBm（Fractal Brownian Motion）
日本語だと非整数ブラウン運動と言うらしい

`このテクニックはプログラムで地形を作り出すのによく用いられています。fBMの自己相似性は、山を形づくる侵食過程の自己相似性に似ているため、山脈の形を再現するのに最適です。 -The Book of Shaders`
ふむふむなるほど

`自己相似性と長期依存(long range dependence)を特徴とするガウス過程 - Wikipedia`
完全に理解した

#早速遊んでみる
The Book of ShadersのシェーダをQuadに貼り付けて画面全体に描画した感じです
ウネウネ感がきもちいい
![fbm.gif](https://qiita-image-store.s3.amazonaws.com/0/193550/75fea313-9b23-aaf0-e146-1fa268bf13c3.gif)


##ソースコード
[以前](https://qiita.com/Ryopon/items/54b9d4649d8ac16f844f)作ったcgincに追加する形でfBm関数を作成しています
インスペクタで設定する`Col1~3`の色を変えていくことでお好みのカラーリングに調整出来るようにしています。

```glsl:Noise.cginc
float random(float2 co) {
    return frac(sin(dot(co.xy, float2(12.9898, 78.233))) * 43758.5453123);
}

float random(float3 co) {
    return frac(sin(dot(co.xyz, float3(12.9898, 78.233, 56.787))) * 43758.5453123);
}

float noise(float2 pos) {
    float2 i = floor(pos);
    float2 f = frac(pos);
    float a = random(i);
    float b = random(i + float2(1.0, 0.0));
    float c = random(i + float2(0.0, 1.0));
    float d = random(i + float2(1.0, 1.0));

    float2 u = f * f * (3.0 - 2.0 * f);

    return lerp(a, b, u.x) +
            (c - a) * u.y * (1.0 - u.x) +
            (d - b) * u.x * u.y;
}

float noise(float3 pos) {
    float3 ip = floor(pos);
    float3 fp = smoothstep(0, 1, frac(pos));
    float4 a = float4(
        random(ip + float3(0, 0, 0)),
        random(ip + float3(1, 0, 0)),
        random(ip + float3(0, 1, 0)),
        random(ip + float3(1, 1, 0)));
    float4 b = float4(
        random(ip + float3(0, 0, 1)),
        random(ip + float3(1, 0, 1)),
        random(ip + float3(0, 1, 1)),
        random(ip + float3(1, 1, 1)));
    a = lerp(a, b, fp.z);
    a.xy = lerp(a.xy, a.zw, fp.y);
    return lerp(a.x, a.y, fp.x);
}

//pseudo perlin noise
float perlin(float3 pos) {
    return  (noise(pos) * 32 +
            noise(pos * 2 ) * 16 +
            noise(pos * 4) * 8 +
            noise(pos * 8) * 4 +
            noise(pos * 16) * 2 +
            noise(pos * 32) ) / 63;
}

float perlin(float2 pos) {
    return  (noise(pos) * 32 +
            noise(pos * 2 ) * 16 +
            noise(pos * 4) * 8 +
            noise(pos * 8) * 4 +
            noise(pos * 16) * 2 +
            noise(pos * 32) ) / 63;
}

//fractal brownian motion
#define OCTAVES 5
float fbm(float2 pos) {
    float value = 0.0;
    float amplitude = .3;
    float frequency = 0.;

    for(int i = 0; i < OCTAVES; i++) {
        value += amplitude * noise(pos);
        pos *= 2.;
        amplitude *= .7;
    }
    return value;
}
```

```glsl:FBM.shader
Shader "Custom/FBM"
{
	Properties
	{
		_MainTex ("Texture", 2D) = "white" {}

		_Col1("col1", Color) = (1, 1, 1, 1)
		_Col2("col2", Color) = (1, 1, 1, 1)
		_Col3("col3", Color) = (1, 1, 1, 1)
	}
	SubShader
	{
		Tags { "RenderType"="Opaque" }
		LOD 100

		Pass
		{
			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag
		
			#include "UnityCG.cginc"
			#include "Noise.cginc"

			struct appdata
			{
				float4 vertex : POSITION;
				float2 uv : TEXCOORD0;
			};

			struct v2f
			{
				float2 uv : TEXCOORD0;
				float4 vertex : SV_POSITION;
				float4 screenPos : TEXCOORD1;
			};

			sampler2D _MainTex;
			float4 _MainTex_ST;
			fixed4 _Col1, _Col2, _Col3;
			
			v2f vert (appdata v)
			{
				v2f o;
				o.vertex = UnityObjectToClipPos(v.vertex);
				o.uv = TRANSFORM_TEX(v.uv, _MainTex);
				o.screenPos = ComputeScreenPos(o.vertex);
				return o;
			}
			
			fixed4 frag (v2f i) : SV_Target
			{
				float2 uv = float2(i.screenPos.xy * 5 / i.screenPos.w);
				//float2 uv = i.uv; //テクスチャUVでやるならこっち
				fixed3 col = 0.;

				float2 q = 0.;
				q.x = fbm(uv + 0.00 * _Time.y);
				q.y = fbm(uv + float2(1., 1.));

				float2 r = 0.;
				r.x = fbm(uv + 1. * q + float2(1.7, 9.2) + 0.15 * _Time.y);
				r.y = fbm(uv + 1. * q + float2(8.3, 2.8) + 0.126 * _Time.y);

				float f = fbm(uv + r);

				col = lerp(_Col1,
						   _Col2,
						   clamp((f * f) * 4.0, 0.0, 1.0));

				col = lerp(col,
							 float3(0., 0., 0.164706),
							 clamp(length(q), 0.0, 1.0));

				col = lerp(col,
							 _Col3,
							 clamp(length(r.x), 0.0, 1.0));

				return fixed4((f * f * f + .6 * f * f + .5 * f) * col, 1.);
			}
			ENDCG
		}
	}
}
```

#オマケ（fBmを使った雲っぽい何か）
頂点アニメーションや法線にfBmを使えば雲っぽいモクモクが作れるのでは！と思ったけどイマイチ思うようにいかなかったので供養
頂点アニメーションを使っている関係上、ハイポリのスフィアを使っています
![cloud.gif](https://qiita-image-store.s3.amazonaws.com/0/193550/8f6885fe-cce7-d06b-125e-ae0edfea1107.gif)

```glsl:Cloud.shader
Shader "Custom/Cloud"
{
    Properties
    {
        _MainTex("Texture", 2D) = "white" {}
        _Color("Color", Color) = (1, 1, 1, 1)
		_RimColor ("Rim Color", Color) = (1, 1, 1, 1)
        _Amplitude("Amplitude", float) = 1.0
        _RimPower("RimPower", Range(0.1, 10.0)) = 3.0
    }

    SubShader
    {
        Tags{ "RenderType"="Transparent" "Queue"="Transparent" }
        Blend SrcAlpha OneMinusSrcAlpha
        CGPROGRAM
        #pragma surface surf Lambert vertex:vert alpha
        #pragma target 3.0      
        #include "Noise.cginc"

        sampler2D _MainTex;
		fixed4 _Color;
		fixed4 _RimColor;
        float _Amplitude;
        float _RimPower;

        struct Input
        {
            float2 uv_MainTex;
            float3 viewDir;
			float4 screenPos;
        };

        void vert(inout appdata_full v)
        {
            v.vertex.xyz += v.normal * (perlin(v.vertex.xyz + _Time.x) + fbm(v.vertex.xyz + _Time.x)) * _Amplitude;
        }

        void surf(Input IN, inout SurfaceOutput o)
        {
			float2 uv = float2(IN.screenPos.xy / IN.screenPos.w);
			fixed4 col = tex2D(_MainTex, IN.uv_MainTex) * _Color;
            float dotProduct = saturate(dot(normalize(IN.viewDir), o.Normal));
            float rim = pow(1.0 - dotProduct, _RimPower);
            fixed3 rimColor = rim * _RimColor;

			o.Albedo = col.rgb + rimColor;
			o.Normal = fixed3(fbm(IN.uv_MainTex + float2(1.7 - _Time.x, 9.2 + _Time.x) * IN.uv_MainTex + float2(1.7 - _Time.x, 9.2 + _Time.x)),
                fbm(IN.uv_MainTex + float2(1. - _Time.x, 1. - _Time.x) * IN.uv_MainTex + float2(1.7 - _Time.x, 9.2 + _Time.x)),
                fbm(IN.uv_MainTex + float2(8.3 + _Time.x, 2.8 - _Time.x) * IN.uv_MainTex + float2(1.7 - _Time.x, 9.2 + _Time.x)));
            //o.Emission = rimColor;
			o.Alpha = col.a - rim;
        }

        ENDCG
    }
    Fallback "Diffuse"
}
```