先日私がリリースいたしましたUnity向けシェーダー「lilToon」についての話です。
公開直後ながら既に多くのご支援をいただいており、心より感謝申し上げます。
今回の記事はlilToon向けの話になりますが、BlendingやステンシルやVR向けのシェーダー処理の解説も含まれており、他シェーダーにも応用できる内容も含まれておりますので是非ご活用ください。
目次
Blending関連
乗算合成
-
透過モード
を半透明
にする - 詳細設定から
基本設定
を開く -
Unlit化
を1にする - 詳細設定から
レンダリング設定
を開く -
Forward
を開きSrcBlend RGB
をZero
に、DstBlend RGB
をSrcColor
にする -
ForwardAdd
を開きSrcBlend RGB
をZero
に、DstBlend RGB
をOne
にする
(解説)
まず、二重にライティングされて暗くなるのを防ぐためにUnlitのシェーダーを用いる必要があったのでUnlit化しています。
そしてBlendについてですが、Srcがシェーダーで計算した色、Dstが既に描画されている色を表しています。
『SrcBlend RGB
をZero
に、DstBlend RGB
をSrcColor
にする』という操作で、Src * Zero + Dst * SrcColor = Dst * SrcColor
となり乗算合成となるわけです。
ステンシル関連
照れシェイプキーや前髪の影を肌の上だけに出す
- 肌のマテリアルを選択
- 詳細設定から
ステンシル設定
を開く -
Ref
の数値を0以外にし、Pass
をReplace
にする - 肌の上に描画したい物のマテリアルを選択
-
Ref
の数値を肌で設定したものに合わせ、Comp
をEqual
にする -
レンダリング設定
からRenderQueueの数値を肌のマテリアルより大きい数値にする
オブジェクトの縁だけにアウトラインを出す
- 詳細設定から
ステンシル設定
を開く -
Ref
の数値を2つとも0以外にする(数値は合わせる) - 上にある本体側の
Pass
をReplace
にする - 下にある輪郭線の
Comp
をNotEqual
にする
【2021/07/20追記】ステンシルを半透明にする
ここでは眉毛を髪の毛の上に描画する場合の手順について解説します。
1~6までは普通のステンシル設定なのでわかる方は読み飛ばしてください。
- 眉毛のマテリアルを選択
- 詳細設定から
ステンシル設定
を開く -
Ref
の数値を0以外にし、Pass
をReplace
にする - 髪のマテリアルを選択
-
Ref
の数値を眉で設定したものに合わせ、Comp
をNotEqual
にする -
レンダリング設定
からRenderQueueの数値を肌のマテリアルより大きい数値にする - 髪のマテリアルを複製
- 髪のメッシュの
Materials
のSize
を2に増やし、Element 1
の方に複製したマテリアルを割り当てる - 複製したマテリアルを選択
- 透過モードを半透明にする
- シェーダーを
_lil/[Optional] lilToonOverlay
に変更 -
Ref
の数値を眉で設定したものに合わせ、Comp
をEqual
にする - 透明度を調整する
(解説)
ステンシルというのはスクリーン上で行えるマスク操作のようなものです。
オブジェクトを描画する際に色や奥行き情報が書き出されますが、一緒にステンシルの数値を書き出すことで様々なマスク操作を行うことができます。
Pass
というのは数値をどのように書き換えるかの設定で、例えばKeep
ならもともとあった数値をキープしますし、Replace
ならRef
の数値に置き換えます。
Comp
はどのような条件でオブジェクトを描画するかの設定で、Always
ならオールウェイズ描画してくれますし、NotEqual
ならRef
とイコールじゃないときに描画します。
照れシェイプキーや髪影の設定はアーティスト視点から見ても結構気になる部分なので、クリスタのクリッピング感覚でマスク処理できるというのは知っておくだけで役に立つと思います。
【2021/07/20追記】
ステンシルを半透明にする方法を追記しました。
シェーダーのパス数を増やせば同様のことを行えますが、URPではパイプラインの編集が必要になるためマテリアルを分けています。
今回使用したlilToonOverlayはメインパスのみ使用するシェーダーになっているため普通のシェーダーを利用する場合より負荷を抑えられます。
その他
環境光を無視して常に影を出す
- 詳細設定から
影設定
を開く -
環境光の強さ
を下げる
(解説)
この環境光の強さは影の色だけに影響し、アバター自体の明るさに影響はありません。
lilToonではStandardShaderに近づけるためにEnvironment Lighting
(環境光)を取得して影の色を補正するようになっています。
なので、StandardShaderが明るくなる状況ではlilToonも明るくなって影が薄くなります。
indirectCol = lerp(indirectCol, albedo, environmentLighting * _ShadowEnvStrength);
輪郭線の太さを一定にする
- 詳細設定から
輪郭線設定
を開く -
太さを補正
のチェックを外す
(解説)
距離に応じた太さの補正が無効化されます。
カメラがキャラクターに近づいたときに線が太くなり気になることがありますが、ワールド空間でのカメラ座標と頂点座標の距離を元に太さを補正することでこの問題を緩和できるようになっています。
if(_OutlineFixWidth) outlineWidth *= saturate(length(_WorldSpaceCameraPos.xyz - positionWS.xyz));
ワールドに関係なくガチ恋距離
- 詳細設定から
シェーダー設定
を開く -
距離クリッピングキャンセラー
をオンにしてApply
(解説)
普通にゲームや映像を作っている分にはクリッピング距離はそこまで気になりませんが、VRでは「顔を近づけて物を見る」という場面が出てくるので近づくと消えてしまうのは没入感を下げてしまう要因になるかと思います。
カメラ側でクリッピング距離を小さくしてあげるのがベストですが、クリッピング距離近くなったときにメッシュを圧縮するような処理を加えることでも回避できます。
#if defined(UNITY_REVERSED_Z)
// DirectX
if(output.positionCS.w < _ProjectionParams.y * 1.01 && output.positionCS.w > 0) output.positionCS.z = output.positionCS.z * 0.0001 + output.positionCS.w * 0.999;
#else
// OpenGL
if(output.positionCS.w < _ProjectionParams.y * 1.01 && output.positionCS.w > 0) output.positionCS.z = output.positionCS.z * 0.0001 - output.positionCS.w * 0.999;
#endif
アルファマスクを使いたい
焼き込みのみの対応となっていたため、次バージョンで追加いたします。
焼き込みは透過マテリアルにした際にメインテクスチャの下に現れるアルファマスクを焼き込み
から行えます。
【2021/07/16追記】
lilToon v1.1にてアルファマスクに対応いたしました。
アルファマスクを割り当て後にスライダーで透明度の調整、調整したテクスチャの焼き込みが行えます。
顔だけ明るかったり暗かったりする
開発中から気になっていたのですが、これはNot Importantなスポットライト(頂点ライト)では形状を考慮できないというUnityの仕様によるものです。
次バージョンで頂点ライティングを無効化するプロパティを追加して、Avatar3.0のメニューなどから状況に応じてオンオフできるようにします。
回避策が見つかり次第修正したいところですが、ポイントライト・スポットライトを区別できる変数はなさそう……?
【2021/07/16追記】
lilToon v1.1にて頂点ライトの強度が調整可能になりました。
基本設定より頂点ライトの強度
を0にすることでこの問題の修正ができます。