📑

[WIP]アウトラインシェーダを考える

2021/10/16に公開

この記事について

ここではこのアウトラインシェーダの仕組みと主な手法、問題点をUnityを使って考えていきます。

アウトラインシェーダとは

アウトラインは端的に言えばモデル(オブジェクト)に対して周囲を取り囲むように表示される線の事で輪郭線とも言われています。
このアウトラインをUnityで表現する場合は主にシェーダを用いて表現されることが多々あり、アウトラインシェーダと呼ばれています。
アウトラインシェーダを用いることで、以下のような表現が出来ます。

altテキスト
シーン上での選択アウトライン(オレンジ色)

altテキスト
Unityちゃんのフェイスアウトライン(左:オフ、右:オン)

単純な拡大で生成する方法の基本

もっとも簡単にアウトラインを表示する方法は、モデルを一回り大きく描写してその上にモデルを描写する方法です。
しかしこの方法では問題が多々あり、シンプルなモデルでしか使えません。
ここでは画像を用いて単純に拡大する方法を確認してみます。

altテキスト
単純な拡大によるアウトライン生成

一段目の四角形は問題なくアウトラインが表現されているますが二段目の画像では右側のアウトラインが出ていません。また、三段目の画像でも内側にアウトラインが出ていないことが分かります。
この方法は原点から拡大をするため、原点との距離でアウトラインの幅が変わってしまったり、原点の方を向いている辺ではアウトラインがモデルの内側に入り込んでしまう問題があります。

altテキスト

対策として各々の辺から外側に向かってアウトラインを生成すれる方法があれば解決できそうです。
それではこの方法を実現するためにシェーダを使いアウトラインを生成してみます。

シェーダで法線方向に拡大する方法

基本的には先ほどの画像の拡大と考え方は一緒です。
違いは「原点から拡大」しているのではなく、「法線方向に拡大」している点です。

こちらが今回のシェーダコードです。

コード

Shader "Unlit/NormalOutLine"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
        _BaseColor("BaseColor", Color) = (1, 1, 1, 1)
        _OutlineColor("Color", Color) = (1, 1, 1, 1)
        _OutlineWidth("Width", float) = 1
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 100

        Pass // 1
        {
            cull Front
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"

            struct appdata
            {
                float4 vertex : POSITION;
                float3 normal : NORMAL;
            };

            struct v2f
            {
                float4 vertex : SV_POSITION;
            };

            half _OutlineWidth;
            half4 _OutlineColor;
            
            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex +  v.normal * _OutlineWidth / 100);
                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                return _OutlineColor;
            }
            ENDCG
        }
        
        Pass // 2
        {
            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;
            };

            half4 _BaseColor;

            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                return _BaseColor;
            }
            ENDCG
        }
    }
}

それではこのシェーダを適応してみましょう。
早速インスペクター上で[3D Object > Cube]を選択してキューブを、続けて[3D Object > Sphere]を選択してスフィアを作成してこの2つのモデルに上記のNormalOutLine.shaderを適応します。

altテキスト
キューブとスフィアのアウトライン生成

スフィアは問題ありませんが、キューブのアウトラインが切れてしまっています。
これは頂点が持っている法線データの違いによるものです。

Unityでは手軽に法線データを確認できないため、Blenderでモデルの法線データ確認してみます。

altテキスト
キューブと頂点が持つ面ごとの法線(ピンク色の線)

各頂点から面ごとの法線を表示してみました。確かに面から垂直に法線が引かれているため法線方向に拡大すると角や辺の付近にはアウトラインが生成されることはなさそうです。
Blender上で板ポリをおいて実際に法線が垂直な場合をシミュレートした結果がこちらです。

altテキスト
垂直な法線のシミュレーション

確かにUnity上で見た状態になりました。

次は頂点の法線データをBlender編集してUnityに持っていき、想定しているアウトラインが出るか確認します。
簡略化のため、一時的にキューブの1面だけで考え、予想されるアウトラインをオレンジの板て表現しています。

altテキスト
法線が面に対して垂直な状態

現在はこのように面に対して垂直な法線になっているため、アウトラインが角までカバーできていない状態です。

altテキスト
法線が関連する面の平均ベクトルの状態

そこで上記のように角の法線を関連する面の平均ベクトルになるように編集しました。

altテキスト
法線修正後の面ごとの法線(ピンク色の線)

平均ベクトルの法線であれば、このシミュレーション通りになるはずです。

altテキスト
平均ベクトルの法線のシミュレーション

それではUnityで確認をしてみましょう。

altテキスト
モデル修正前(左)とモデル修正後(右)

無事にキューブのアウトラインを作ることが出来ました。
つまり原因は法線データが面から垂直になため、角や辺の部分にアウトラインが生成できていないという事でした。

スフィアはなぜ綺麗なアウトラインが出来ていたかというと、こちらは元から法線が面ごとに垂直になっておらず、平均ベクトルとなっていたためです。

問題点

それではこのシェーダを用いれば完璧なアウトラインを表現できるでしょうか?
残念ながらそううまくはいきません。

重なりに対応できない

altテキスト
重なった際にアウトラインにめり込んでいる

複雑なモデルに対応できない

altテキスト
表示が崩れているUnityちゃんの手のアウトライン

参考サイト

https://light11.hatenadiary.com/entry/2018/05/13/183314

ライセンス表記

© Unity Technologies Japan/UCL

GitHubで編集を提案

Discussion