先日私がリリースいたしました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にすることでこの問題の修正ができます。