[WIP]アウトラインシェーダを考える
この記事について
ここではこのアウトラインシェーダの仕組みと主な手法、問題点をUnityを使って考えていきます。
アウトラインシェーダとは
アウトラインは端的に言えばモデル(オブジェクト)に対して周囲を取り囲むように表示される線の事で輪郭線とも言われています。
このアウトラインをUnityで表現する場合は主にシェーダを用いて表現されることが多々あり、アウトラインシェーダと呼ばれています。
アウトラインシェーダを用いることで、以下のような表現が出来ます。
シーン上での選択アウトライン(オレンジ色)
Unityちゃんのフェイスアウトライン(左:オフ、右:オン)
単純な拡大で生成する方法の基本
もっとも簡単にアウトラインを表示する方法は、モデルを一回り大きく描写してその上にモデルを描写する方法です。
しかしこの方法では問題が多々あり、シンプルなモデルでしか使えません。
ここでは画像を用いて単純に拡大する方法を確認してみます。
単純な拡大によるアウトライン生成
一段目の四角形は問題なくアウトラインが表現されているますが二段目の画像では右側のアウトラインが出ていません。また、三段目の画像でも内側にアウトラインが出ていないことが分かります。
この方法は原点から拡大をするため、原点との距離でアウトラインの幅が変わってしまったり、原点の方を向いている辺ではアウトラインがモデルの内側に入り込んでしまう問題があります。
対策として各々の辺から外側に向かってアウトラインを生成すれる方法があれば解決できそうです。
それではこの方法を実現するためにシェーダを使いアウトラインを生成してみます。
シェーダで法線方向に拡大する方法
基本的には先ほどの画像の拡大と考え方は一緒です。
違いは「原点から拡大」しているのではなく、「法線方向に拡大」している点です。
こちらが今回のシェーダコードです。
コード
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を適応します。
キューブとスフィアのアウトライン生成
スフィアは問題ありませんが、キューブのアウトラインが切れてしまっています。
これは頂点が持っている法線データの違いによるものです。
Unityでは手軽に法線データを確認できないため、Blenderでモデルの法線データ確認してみます。
キューブと頂点が持つ面ごとの法線(ピンク色の線)
各頂点から面ごとの法線を表示してみました。確かに面から垂直に法線が引かれているため法線方向に拡大すると角や辺の付近にはアウトラインが生成されることはなさそうです。
Blender上で板ポリをおいて実際に法線が垂直な場合をシミュレートした結果がこちらです。
垂直な法線のシミュレーション
確かにUnity上で見た状態になりました。
次は頂点の法線データをBlender編集してUnityに持っていき、想定しているアウトラインが出るか確認します。
簡略化のため、一時的にキューブの1面だけで考え、予想されるアウトラインをオレンジの板て表現しています。
法線が面に対して垂直な状態
現在はこのように面に対して垂直な法線になっているため、アウトラインが角までカバーできていない状態です。
法線が関連する面の平均ベクトルの状態
そこで上記のように角の法線を関連する面の平均ベクトルになるように編集しました。
法線修正後の面ごとの法線(ピンク色の線)
平均ベクトルの法線であれば、このシミュレーション通りになるはずです。
平均ベクトルの法線のシミュレーション
それではUnityで確認をしてみましょう。
モデル修正前(左)とモデル修正後(右)
無事にキューブのアウトラインを作ることが出来ました。
つまり原因は法線データが面から垂直になため、角や辺の部分にアウトラインが生成できていないという事でした。
スフィアはなぜ綺麗なアウトラインが出来ていたかというと、こちらは元から法線が面ごとに垂直になっておらず、平均ベクトルとなっていたためです。
問題点
それではこのシェーダを用いれば完璧なアウトラインを表現できるでしょうか?
残念ながらそううまくはいきません。
重なりに対応できない
重なった際にアウトラインにめり込んでいる
複雑なモデルに対応できない
表示が崩れているUnityちゃんの手のアウトライン
参考サイト
ライセンス表記
© Unity Technologies Japan/UCL
Discussion