知識0からのUnityShader勉強

知識0からのUnityShader勉強

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

【UnityShader】リムライティング #8

前回の成果

f:id:soramamenatan:20190621154313p:plain

ドラゴンが氷っぽくなった。

soramamenatan.hatenablog.com


今回やること

nn-hokuson.hatenablog.com

ドラゴンをリムライティングっぽくします!
前回とやることはほぼ同じそうですね。


ちなみにリムライティングとは、

f:id:soramamenatan:20190624182551p:plain

【Unityシェーダ入門】リムライティングのシェーダを作る - おもちゃラボ:より引用

このように、モデルの後方からライトが当たっている状態を再現することです。

ではやっていきましょう!


ソースコード

Shader "Custom/rimlighting" {
    SubShader {
        Tags { "RenderType" = "Opaque"}
        LOD 200

        CGPROGRAM
        #pragma surface surf Standard
        #pragma target 3.0

        struct Input {
            float3 worldNormal;
            float3 viewDir;
        };

        void surf(Input IN, inout SurfaceOutputStandard o) {
            fixed4 baseColor = fixed4(0.05f,0.1f,0,1.0f);
            fixed4 rimColor = fixed4(0.5f,0.7f,0.5f,1.0f);

            o.Albedo = baseColor;
            float rim = 1 - saturate(dot(IN.viewDir,IN.worldNormal));
            o.Emission = rimColor * pow(rim, 2);
        }
        ENDCG
    }
    FallBack "Diffuse"
}

ソースコードも前回とほぼ同じですね。
異なっている部分は

float rim = 1 - saturate(dot(IN.viewDir,IN.worldNormal));
o.Emission = rimColor * pow(rim, 2);

この部分と

Tags { "RenderType" = "Opaque"}

この部分です。
Emissonってなんだっけ、と忘れてしまった方 、発行度合いのことです。
詳しくはこちらをどうぞ。

soramamenatan.hatenablog.com


saturate??pow??

まずは、この2つのキーワードについて説明していきたいと思います。


saturate

引数に持たせた数字を0〜1の間に収める関数です。

int a = -10;
int b = 2;
float c = 0.5f;

// 0が返ってくる
saturate(a);

// 1が返ってくる
saturate(b);

// 0.5が返ってくる
saturate(c);


pow

// xのy乗返す
pow(x,y)

いちいち*を書かなくて良いのが便利そうですね。


疑問点

察しの良い人なら気付いていると思いますが、今回と前回で2つの疑問点が浮かんでいると思います。

  • absとsaturate、結局カメラから裏見えないからどっちでも良い?
  • SurfaceOutputStandardにもNormalなかった?

この2つです。

absとsaturate、結局カメラから裏見えないからどっちでも良い?

正直、どっちでも良いと思います。
しかし、今回のようなソースを書き、裏が完全に見えてしまうならsaturateの方が裏も正しい計算で通るのでそちらの方がよいかと。

SurfaceOutputStandardにもNormalなかった?

実はその他にももう1種類あります。

名称 用途
SurfaceOutput Normal 書き込まれる場合は、接地空間法線
input worldNormal サーフェスシェーダーがSurfaceOutput Normalに書き込まない場合のワールドの法線ベクトルを含む
input worldNormal; INTERNAL_DATA サーフェスシェーダーがSurfaceOutput Normalに書き込む場合のワールドの法線ベクトルを含む。※ピクセル法線マップに基づいて法線ベクトルを取得するには、WorldNormalVector(IN, o.Normal)を使用する

となっています。
今回のケースの場合、SurfaceOutput Normalに書き込まないのでどれを使用しても同じです。

詳しくはこちらのサイト様へ docs.unity3d.com


RenderType

今まではQueueで描画順を指定していたと思います。
RenderTypeはどのように描画するかを決めるものです。

名称 用途
Opaque ほとんどのシェーダー
Transparent ほとんどが透過しているシェーダー(パーティクル等)
TransparentCutout マスキングされた透過シェーダー
Background Skybox
Overlay GUITexture、ハロー、フレアシェーダー
TreeOpaque Terrainの樹皮
TreeTransparentCutout Terrainの木の葉
TreeBillboard Terrainのビルボードされた木
Grass Terrainの草
GrassBillboard Terrainのビルボードされた草

となっています。
今回は特に特別な描画方法を使用しないので、Opaqueを使用します。
詳しい説明はこちらのサイト様に記述してあります。

docs.unity3d.com


不明な単語集

いくつかなんだそれ、となる言葉が出てきたと思うので解説していきたいと思います。


WorldNormalVector

法線情報をオブジェクトのローカル座標からワールド座標に変換するための関数です。

ハロー

ハロー(Halo)とは、光源周辺の明るいエリアを細かい粉塵で表現しているものです。

f:id:soramamenatan:20190626145657j:plain Halo - Unity マニュアル:より引用

docs.unity3d.com

フレアシェーダー

原文通り、flare shadersで検索してもヒットしなかったのでおそらくレンズフレアのことかなと思います。

tsubakit1.hateblo.jp

Terrain

手軽に地形を作成できるUnityの機能の1つです。

f:id:soramamenatan:20190626145558p:plain 【Unity】Terrainで背景っぽい地形を作ってみる | ゴマちゃんフロンティア:より引用

qiita.com

ビルボード

2Dのテクスチャが3D空間で常にカメラの方向を向くようにすることです。

nn-hokuson.hatenablog.com


今回の成果

f:id:soramamenatan:20190624182237p:plain

少しわかりにくいですが、ドラゴンの輪郭が光っています。


コメントを付与

Shader "Custom/rimlighting" {
    // あとで理解する
    SubShader {
        // 一般的なシェーダーを使用
        Tags { "RenderType" = "Opaque"}
        // しきい値
        LOD 200

        // あとで理解する
        CGPROGRAM
        // あとで理解する
        #pragma surface surf Standard
        // あとで理解する
        #pragma target 3.0

        // input構造体
        struct Input {
            // 1ピクセル毎の法線
            float3 worldNormal;
            // カメラからの視線
            float3 viewDir;
        };

        // surf関数
        void surf(Input IN, inout SurfaceOutputStandard o) {
            // ベースカラーの変更
            fixed4 baseColor = fixed4(0.05f,0.1f,0,1.0f);
            // リムライティングの色を決める
            fixed4 rimColor = fixed4(0.5f,0.7f,0.5f,1.0f);

            o.Albedo = baseColor;
            // リムライティングの度合いを法線と視線の2つのベクトルから取得
            float rim = 1 - saturate(dot(IN.viewDir,IN.worldNormal));
            // 発行度合いを調整する
            o.Emission = rimColor * pow(rim, 2);
        }
        // あとで理解する
        ENDCG
    }
    // あとで理解する
    FallBack "Diffuse"
}

今回は以上となります。
次回もおもちゃラボ様を参考にシェーダーの勉強をしていきます!