Unityでオブジェクトの アウトラインを発光させてサイバー感を演出する

下の画像は今作っているVR音ゲーのSSなのですが、ステージのアウトラインが発光しており、サイバー感がでている。。。と自分では思っています。
自分では結構お気に入りの演出で、今回の題材にしようと思います。

https://c10.patreonusercontent.com/3/eyJ3Ijo2MjB9/patreon-media/p/post/23677643/f395bc7a2e754c5292fba47aff8a536f/1?token-time=2145916800&token-hash=ooQW8wO2pPlUgSQefLf7jS-30jCS-6d5xywH8PWcyTk%3D

以下動画の4:20位にジャンプすると、上記SSのシーンに飛べます。
www.nicovideo.jp

まずは、アウトラインを設定できるシェーダーをマテリアルに設定する必要があります。
以下はUnityちゃんトゥーンシェーダー2.0(以下UTS2.0)の設定で、今回はこれを使って説明します。
「Outline_Width」、「Outline_Color」を設定するとアウトラインが生成されます。
f:id:rins64th:20190120222753p:plain

がしかし、「Outline_Color」には発光用のインスペクタが用意されていません。
f:id:rins64th:20190120223049p:plain

「Emissive_Color」では以下のようにHDR設定が可能ですが、これはシェーダーの記述法によるようです。
f:id:rins64th:20190120223411p:plain

UTS2.0ではそれぞれのプロパティは以下のように記述されています。

[HDR]_Emissive_Color ("Emissive_Color", Color) = (0,0,0,1)
_Outline_Color ("Outline_Color", Color) = (0.5,0.5,0.5,1)

解決方法としてはシェーダー上で「Outline_Color」にも「HDR」属性をつけてしまえばよいかもしれませんが、今回はあえてそれは実施しません。
まず第一に、自分で作っているわけでもないUTS2.0を直接改造したくありません。
また、インスペクター上で直接カラーをいじると、動画のように発光していない状態から徐々に発行させたり、発光に強弱を与えたり、発光色を変えたりといったことができないためです。
ので、ここではスクリプトからいじれるようにすることで、汎用性を出します。

スクリプトからシェーダープロパティを編集するざっくり手順です。
①対象オブジェクトのマテリアルを取得しておく。
②上記マテリアルに対し、SetXXXメソッドでシェーダープロパティを保存する。
※以下サンプルではオブジェクトのマテリアルを取得する処理を端折っています(いつか別記事にする予定)

    private Material[] _Materials;
    private int _OutlineId;
    private Color? _OutlineColor;
    private float _OutlineEmission;

    private void Awake()
    {
        _OutlineId = Shader.PropertyToID("_Outline_Color");
    }

    private void RelectMeshes()
    {
        for(int i = 0; i < _Materials.Length; i++)
        {
            if (_OutlineColor == null) _Materials[i].SetColor(_OutlineId, _Materials[i].GetColor(_OutlineId) * _OutlineEmission);
            else _Materials[i].SetColor(_OutlineId, _OutlineColor.Value * _OutlineEmission);
            _Materials[i].SetFloat(_OutlineWidthId, _OutlineWidth);
        }
    }

肝はSetColorメソッド実行時、カラー引数に_OutlineColorと_OutlineEmissionかけ合わせていることです。
これで_OutlineEmissionに1以上を指定することで_OutlineColor色で発光します。
あとはこれをupdateメソッド等で_OutlineEmissionをインクリメントしながらRelectMeshesを呼び出せば、徐々に発行するようスクリプトを組めます。

おまけ

マテリアルにSetXXXメソッドを適用する際、引数にはプロパティ名(string)で渡すよりも、ID値(int)で渡すほうが処理負荷が軽いようです。
結局内部的にはプロパティ名(string)からID値(int)に変換するらしいので。


また、今回はUTS2.0で説明しましたが、他のシェーダーでも使えると思います。(UTS2.0は重いです)
その際、プロパティ名は異なるため、実際のシェーダー上で確認してください。
以下のように定義されているはずなので、インスペクタ上の名前から実際のプロパティ名を確認できます。

_Outline_Color ("Outline_Color", Color) = (0.5,0.5,0.5,1)

今回は以上です。