知識0からのUnityShader勉強

知識0からのUnityShader勉強

UnityのShaderをメインとして、0から学んでいくブログです。

【UnityShader】3種類のノイズ #16

前回の成果

soramamenatan.hatenablog.com

リングの位置を追従させた。

今回やること

nn-hokuson.hatenablog.com

今回は、ノイズを制作していきます。

前準備

f:id:soramamenatan:20190809111346p:plain

PlaneをScene上に置き、今回制作するMaterialをアタッチしてください。

ランダムノイズの制作

ランダムノイズとは、テレビの砂嵐のようなものです。

f:id:soramamenatan:20190809160346j:plain

テレビが砂嵐になって壊れた!?と思ったら、まず確認すべきこと | Hinemosu:より引用

ソースコード

Shader "Custom/randomNoise" {
    Properties {
        _MainTex("Albedo(RBG)", 2D) = "white" {}
    }
    SubShader {
        Tags { "RenderType" = "Opaque"}
        LOD 200

        CGPROGRAM
        #pragma surface surf Standard fullforwardshadows
        #pragma target 3.0

        struct Input {
            float2 uv_MainTex;
        };

        float random(fixed2 uv) {
            return frac(sin(dot(uv, fixed2(12.9898f, 78.233f))) * 43758.5453);
        }

        void surf(Input IN, inout SurfaceOutputStandard o) {
            float randomNum = random(IN.uv_MainTex);
            o.Albedo = fixed4(randomNum, randomNum, randomNum, 1);
        }
        ENDCG
    }
    Fallback "Diffuse"
}
float random(fixed2 uv) {
    return frac(sin(dot(uv, fixed2(12.9898f, 78.233f))) * 43758.5453);
}

ここの部分でなぜこのような数値を出しているのかが謎です。

frac

これは小数値の小数部分を返す関数です。

float a = 1.0f;
float b = 1.5f;
// 0が返ってくる
frac(a);
// 0.5が返ってくる
frac(b);

逆に、小数値の整数を返す関数もあります。

float a = 1.0f;
float b = 3.5f;
// 1が返ってくる
floor(a);
// 3が返ってくる
floor(b);

frac(sin(dot(uv, fixed2(12.9898f, 78.233f))) * 43758.5453)

これはランダムな値を返すものです。

float rand(vec2 co){
    return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453);
}

GLSLでこのような擬似ランダム関数があるので、それをHLSLに変更させたものです。

詳しくはこちらのサイト様へ

qiita.com

結果

f:id:soramamenatan:20190809155758p:plain

ランダムノイズの完成です。

ブロックノイズ

次はブロックノイズを制作していきます。

f:id:soramamenatan:20190809161513j:plain

テレビの映りが悪い!ブロックノイズの原因と対策 | あさひアンテナ:より引用

ソースコード

Shader "Custom/valueNoise" {
    Properties {
        _MainTex("Albedo(RBG)", 2D) = "white" {}
        // 変更点
        _SideBlockNum("SideBlockNum", int) = 0
    }
    SubShader {
        Tags { "RenderType" = "Opaque"}
        LOD 200

        CGPROGRAM
        #pragma surface surf Standard fullforwardshadows
        #pragma target 3.0

        struct Input {
            float2 uv_MainTex;
        };

        int _SideBlockNum;

        float random(fixed2 uv) {
            return frac(sin(dot(uv, fixed2(12.9898f, 78.233f))) * 43758.5453);
        }

        float noise(fixed2 uv) {
            fixed2 uv_integer = floor(uv);
            return random(uv_integer);
        }

        void surf(Input IN, inout SurfaceOutputStandard o) {
            float randomNum = noise(IN.uv_MainTex * _SideBlockNum);
            // ここまで
            o.Albedo = fixed4(randomNum, randomNum, randomNum, 1);
        }
        ENDCG
    }
    Fallback "Diffuse"
}

ランダムノイズから関数が1つ増えています。

解説

行なっていることは、先ほど制作したランダムノイズからブロックごとに代表的な1点を取り出しています。

f:id:soramamenatan:20190809163641p:plain

【Unityシェーダ入門】シェーダで作るノイズ5種盛り - おもちゃラボ:より引用

その1点は、floorメソッドを使用して決めています。 例を出すと、
座標が(0.8,0.8)なら(0,0)に
(3.2,1.6)なら(3,1)になります。
今回は、テクスチャの座標をSideBlockNum倍しているので、SideBlockNum×_SideBlockNumのブロックノイズが生成されます。

f:id:soramamenatan:20190809164146p:plain

【Unityシェーダ入門】シェーダで作るノイズ5種盛り - おもちゃラボ:より引用

結果

f:id:soramamenatan:20190809164333p:plain

_SideBlockNumには5を指定しています。

バリューノイズ

f:id:soramamenatan:20190809164728p:plain

The Book of Shaders: Noise:より引用

ブロックノイズを濁したようなノイズです。

ソースコード

Shader "Custom/valueNoise" {
    Properties {
        _MainTex("Albedo(RBG)", 2D) = "white" {}
        _SideBlockNum("SideBlockNum", int) = 0
    }
    SubShader {
        Tags { "RenderType" = "Opaque"}
        LOD 200

        CGPROGRAM
        #pragma surface surf Standard fullforwardshadows
        #pragma target 3.0

        struct Input {
            float2 uv_MainTex;
        };

        int _SideBlockNum;

        float random(fixed2 uv) {
            return frac(sin(dot(uv, fixed2(12.9898f, 78.233f))) * 43758.5453);
        }

        float noise(fixed2 uv) {
            fixed2 uv_integer = floor(uv);
            return random(uv_integer);
        }

        // 変更点
        float valueNoise(fixed2 uv) {
            fixed2 uv_integer = floor(uv);
            fixed2 uv_decimal = frac(uv);

            float v00 = random(uv_integer + fixed2(0, 0));
            float v10 = random(uv_integer + fixed2(1, 0));
            float v01 = random(uv_integer + fixed2(0, 1));
            float v11 = random(uv_integer + fixed2(1, 1));
            fixed2 lerpResult = (- 2.0f * pow(uv_decimal, 3)) + (3.0f * pow(uv_decimal, 2));

            float v0010 = lerp(v00, v10, lerpResult.x);
            float v0111 = lerp(v01, v11, lerpResult.x);
            return lerp(v0010, v0111, lerpResult.y);
        }

        void surf(Input IN, inout SurfaceOutputStandard o) {
            float randomNum = valueNoise(IN.uv_MainTex * _SideBlockNum);
            // ここまで
            o.Albedo = fixed4(randomNum, randomNum, randomNum, 1);
        }
        ENDCG
    }
    Fallback "Diffuse"
}

ブロックノイズから更に関数が1つ増えています。

解説

ほぼおもちゃラボ様の引用です。

色の出し方

バリューノイズは、ブロックノイズで描画された、正方形の四隅の色を取得します。 その色から正方形内の色を補間することによって表現されます。

f:id:soramamenatan:20190809171529p:plain

【Unityシェーダ入門】シェーダで作るノイズ5種盛り - おもちゃラボ:より引用

補間の仕方

補間したい座標のオフセット値を求めることによって、表すことができます。
そこで、fracメソッドを使用することによってオフセット値を取得することができます。

f:id:soramamenatan:20190809172627p:plain

【Unityシェーダ入門】シェーダで作るノイズ5種盛り - おもちゃラボ:より引用

補間数式

fixed2 lerpResult = (- 2.0f * pow(uv_decimal, 3)) + (3.0f * pow(uv_decimal, 2));

この部分は、
-2x3 + 3x2
という数式を使用して補間しています。

f:id:soramamenatan:20190809175807p:plain

【Unityシェーダ入門】シェーダで作るノイズ5種盛り - おもちゃラボ:より引用

こちらが-2x3 + 3x2のグラフです。

補間手順

  1. 下の辺(v0010)の色を求める
  2. 上の辺(v0111)の色を求める
  3. 1,2で求めた2点を補間する

f:id:soramamenatan:20190809180219p:plain

【Unityシェーダ入門】シェーダで作るノイズ5種盛り - おもちゃラボ:より引用

結果

f:id:soramamenatan:20190809180354p:plain

今回はここまでとなります。
次回もシェーダーの勉強をしていこうと思います!