知識0からのUnityShader勉強

知識0からのUnityShader勉強

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

【UnityShader】お絵かきシェーダー【1】 #53

前回の成果

Imageのアウトラインの数を指定した。

soramamenatan.hatenablog.com


今回やること

表現の幅を増やすためにお絵かきシェーダーを学んでいきます。

docs.google.com


事前準備

Scene上にQuadを配置します。

f:id:soramamenatan:20200516162721p:plain


ソースコード

Shader "Unlit/Drawing_1" {
    Properties {
        _MainTex ("Texture", 2D) = "white" {}
    }
    SubShader {
        Tags { "RenderType"="Opaque" }

        Pass {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"

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

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

            sampler2D _MainTex;
            float4 _MainTex_ST;

            v2f vert (appdata v) {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);
                return o;
            }

            fixed4 frag (v2f i) : SV_Target {
                return float4(i.uv.x, i.uv.y, 0, 1);
            }
            ENDCG
        }
    }
}

フラグメントシェーダーを変更して表現していきます。

UV値に合わせた色を出す

fixed4 frag (v2f i) : SV_Target {
    return float4(i.uv.x, i.uv.y, 0, 1);
}

UVの座標に応じて色を変化させています。
UV値は左下が(0, 0)、右下が(1, 1)となっているので以下のような結果となります。

UV値のイメージ

f:id:soramamenatan:20200516163655p:plain

結果

f:id:soramamenatan:20200516163700p:plain


ディスタンスフィールド

ディスタンスフィールドとは、相手との距離を返す関数のことです。
これを使用し、異なる表現をしていきます。

distance

Unityのシェーダーでは、distance関数があるのでそれを使用します。

// xとyとの距離をfloatで返す関数
distance(x, y)

フラグメントシェーダーを以下のように書き換えます。

fixed4 frag (v2f i) : SV_Target {
    float4 col = distance(float2(0.5, 0.5), i.uv);
    return col;
}

UVの単元でも説明しましたが、UV値は左下が(0, 0)、右下が(1, 1)となっています。
ですので(0.5, 0.5)は中央となり、中央との距離を求めています。

結果

f:id:soramamenatan:20200516164505p:plain


step

次に、アルファ値を0と1でくっきり分けていきます。
そのためにstep関数を使用します。

// y >= x なら1.0を、x > y なら0.0を返す
step(x, y)

// 1.0が返ってくる
step(0, 1)
// 0.0が返ってくる
step(0.5, 0.1)

このstep関数を使用し、フラグメントシェーダーを変更します。

fixed4 frag (v2f i) : SV_Target {
    float4 col = distance(float2(0.5, 0.5), i.uv);
    col = step(0.4, col);
    return col;
}

距離が0.4までの部分を黒く、それ以上のところは白くすることができます。

結果

f:id:soramamenatan:20200516165040p:plain

また、stepで指定したしきい値サインカーブを使用することによりアニメーションさせることができます。

fixed4 frag (v2f i) : SV_Target {
    float4 col = distance(float2(0.5, 0.5), i.uv);
    col = step(abs(sin(_Time.y)) * 0.4, col);
    return col;
}
結果

f:id:soramamenatan:20200516165714g:plain


複数の円を出す

複数の円を出していきます。
考え方がわかりやすいようにグラフも添付します。


最初にディスタンスフィールドで描画したものが下記になります。

描画したもの

f:id:soramamenatan:20200516164505p:plain

グラフ

f:id:soramamenatan:20200517163128p:plain

ソースコード
fixed4 frag (v2f i) : SV_Target {
    float4 col = distance(float2(0.5, 0.5), i.uv);
    return col;
}


次に適当に30倍に拡大してみます。

描画したもの

f:id:soramamenatan:20200517163403p:plain

グラフf:id:soramamenatan:20200517163425p:plain
ソースコード
fixed4 frag (v2f i) : SV_Target {
    float4 col = distance(float2(0.5, 0.5), i.uv) * 30;
    return col;
}


次にサインカーブをして、その絶対値を取ります。
このようにすることにより、繰り返しとなり円が複数描画されます。

描画したもの

f:id:soramamenatan:20200517163711p:plain

グラフ

f:id:soramamenatan:20200517163737p:plain

ソースコード
fixed4 frag (v2f i) : SV_Target {
    float4 col = distance(float2(0.5, 0.5), i.uv) * 30;
    col = abs(sin(col));
    return col;
}


最後にstepを使用してくっきりとさせます。

描画したもの

f:id:soramamenatan:20200517164214p:plain

グラフ

f:id:soramamenatan:20200517164208p:plain

ソースコード
fixed4 frag (v2f i) : SV_Target {
    float4 col = distance(float2(0.5, 0.5), i.uv) * 30;
    col = abs(sin(col));
    col = step(0.5, col);
    return col;
}

何か表現したいことがあった場合、段階を踏んで描画結果を把握したりグラフを見たりすると早く実装出来ると思います。

グラフはこちらのサイト様のものを使用させていただきました。

www.iquilezles.org

今回は以上となります。
ここまでご視聴ありがとうございました。