SabaShader/Illust2D

3D モデルを 2D イラスト調に見せるトゥーンシェーダーです。 ビルトインレンダーパイプライン(BiRP)向けで、VRChat のアバターを想定しています。

Illust2D の初期値で描画した球体。市松模様は面の向きを確認するためのテクスチャ
Illust2D の初期値で描画した球体。市松模様は面の向きを確認するためのテクスチャ
Blur を 0 にして二値塗りにした同じ球体
Blur を 0 にして二値塗りにした同じ球体

このページの図は、すべて描画回帰テストの ゴールデン画像です。出荷する Illust2DCore.hlsl をそのまま描いたものなので、 数式を変えれば図も変わります。手で描いた説明図ではありません。

考え方

「イラストっぽさ」は影の濃さではなく 影の色 で決まります。 このシェーダーは影を「暗くする」のではなく、ベースカラーの 色相・彩度・明度をずらした別の色に 置き換え ます。 影を少し寒色側にずらして彩度を上げる、という手描きの塗りでよく使われる操作を そのままパラメータにしています。

初期値のランプ表。横軸がライトの当たり具合、縦の 8 段がベースカラー
初期値のランプ表。横軸がライトの当たり具合、縦の 8 段がベースカラー
色相シフトと彩度を強めた設定。同じ明るさでも影の色が変わる
色相シフトと彩度を強めた設定。同じ明るさでも影の色が変わる

パス構成

パスLightMode役割
FORWARDForwardBase本体の塗り
OUTLINEForwardBase反転ハル方式の輪郭線
FORWARD_DELTAForwardAdd追加ライト(加算)
SHADOW_CASTERShadowCaster影の落とし

パラメータ

基本

プロパティ説明
Texture / Colorベースカラー
Normal Map / Roughness法線とラフネス(Shader Core 共通の扱い)
Cull描画する面。両面表示は Off
Alpha Mode / CutoffCutout で Cutoff 未満のピクセルを捨てる
初期値のまま。市松模様は面の向きを見るためのテクスチャ
初期値のまま。市松模様は面の向きを見るためのテクスチャ
平らな面と鋭い稜線での出方
平らな面と鋭い稜線での出方
自己遮蔽のある形。内側に影が落ちる
自己遮蔽のある形。内側に影が落ちる

塗り

影は 2 段(1影・2影)です。ln = dot(N, L) * 0.5 + 0.5 を落ち影で減衰させた値を それぞれの境界とぼかしでランプ化し、albedo → 1影 → 2影 と補間します。

プロパティ説明
Shade 1 / 2 Border影の境界位置。2影の境界は 1影の境界を超えないよう自動で切り詰められます
Shade 1 / 2 Blur境界のぼかし幅。0 で完全な 2 値(アニメ塗り)
Shade 1 / 2 Hue Shift影の色相シフト。イラストらしさの中核
Shade 1 / 2 Saturation / Value影の彩度・明度
Shade 1 / 2 Multiply Color上記を適用した後に掛ける色
Ramp Steps2 以上でぼかし部分を階段状に量子化
Received Shadow Strengthリアルタイム影を塗りに反映する量
Shade Mask Channel共有マスクのどのチャンネルを影マスクに使うか
Blur を 0 にした完全な 2 値塗り(アニメ塗り)
Blur を 0 にした完全な 2 値塗り(アニメ塗り)
広いぼかしに Ramp Steps 4 を重ねた段付き
広いぼかしに Ramp Steps 4 を重ねた段付き
Received Shadow Strength を 0 にして落ち影を無視した状態
Received Shadow Strength を 0 にして落ち影を無視した状態

初期値は「肌色にほんの少し紫を混ぜた 1影」になるよう調整してあります。 硬いアニメ塗りにしたい場合は Blur を 0、Border を 0.55 前後にしてください。

ハイライト

Blinn-Phong の反射をしきい値で切ったハードなハイライトです。 Specular Sharpness が実際の指数(exp2(sharpness * 10 + 1))を決め、 Border / Blur が切り出す形を決めます。

ハイライトとリムライトを強めに出した状態
ハイライトとリムライトを強めに出した状態
押し出した曲面と半球の継ぎ目での伸び方
押し出した曲面と半球の継ぎ目での伸び方

リムライト

1 - dot(N, V) をランプ化したものです。 Follow Light Direction を 1 にするとライトが当たっている側にだけ出ます。 初期値は黒(無効)なので、使う場合は色を設定してください。

輪郭線

法線方向に押し出した反転ハル方式です。

横軸が Base Color Blend、縦の 8 段がベースカラー。右へ行くほど色トレスになる
横軸が Base Color Blend、縦の 8 段がベースカラー。右へ行くほど色トレスになる
プロパティ説明
Outline Width太さ。1.0 でおよそ 1cm 相当
Base Color Blend1 に近づけるほど部位ごとの色に馴染む「色トレス」になる
Outline Hue Shift / Saturation / Value色トレス時のずらし量
Distance Compensation1 で離れても画面上の線幅がほぼ一定になる
Width Mask Vertex Color頂点カラーのどの成分で太さを絞るか(顔まわりを細くする用途)

法線が割れているモデルでは輪郭線も割れます。 モデル側で輪郭線用に法線を整えるか、頂点カラーで該当部分の幅を 0 にしてください。

ライトの受け方

VRChat はワールドごとに明るさがまったく違うため、総照度をクランプします。

横軸が入射光の明るさ。下限 0.25 / 上限 1.2 で折れているのが見える
横軸が入射光の明るさ。下限 0.25 / 上限 1.2 で折れているのが見える
Monochrome Lighting と As Unlit を効かせ、赤いライトの色を落とした状態
Monochrome Lighting と As Unlit を効かせ、赤いライトの色を落とした状態
プロパティ説明
Brightness Min / Max総照度(輝度)の下限・上限
Monochrome Lighting1 でワールドのライト色に染まらなくなる
As Unlit1 でライトを無視してテクスチャそのままの明るさになる
Probe Directional Weightライトプローブの指向性成分を塗りに使う量
Probe Direction Influence影の向きを決める際にライトプローブを考慮する量

ライトの合成方針は「全ライトを 1 本の代表ライトにまとめる」です。 方向は輝度で重み付けした和、色は単純な和を使い、 ライトプローブからは L0/L1 だけを取り出して指向性成分と環境光に分けています。

仕上げ

最終出力に彩度とコントラストを掛けます。 少し上げるとイラストらしい発色になりますが、上げすぎると VRChat の ポストプロセスと喧嘩するので控えめが無難です。

実装ファイル

ファイル中身
Illust2D.scshaderShaderLab(4 パス)
Illust2D_properties.hlslプロパティ定義
sc_common.hlslShader Core が要求するフック(頂点変形・アウトライン押し出し・クリップ)
Illust2DCore.hlslシェーディングの数式(Unity 非依存・テスト対象)
Illust2DLighting.hlslライトの集約とライトプローブの扱い
Illust2DFragment.hlslForwardBase / ForwardAdd のピクセルシェーダー
Illust2DOutlineFragment.hlsl輪郭線のピクセルシェーダー
lang/*.poマテリアルエディタの日本語・英語表示

数式が Illust2DCore.hlsl に切り出してあるのは、 ヘッドレスで描画テストできるようにするためです(docs/testing.md)。