モジュールを追加する
Shader Core のモジュール(.scmodule)は、シェーダー本体が描いた結果の上に 効果を足す仕組みです。本体側が置いた __SC_PHASE_*__ の位置にコードが 差し込まれます。第三者の .scshader にも後から乗せられます。
できること・できないこと
Shader Core の README が明示している制約です。
- パスは追加できません。 アウトラインのような別パスが要る表現は、 モジュールではなくシェーダー本体として作ります。
- 本体や他モジュールが決めた動作は変えられません。 競合するためです。 ベースの絵を消す、ライティングの方向性を変える、といったものは対象外です。
画面全体を加工する表現(合成後のピクセル化や歪み)もできません。 隣接ピクセルを読むには GrabPass が要り、このリポジトリでは使わない方針です。
ファイルの構成
Packages/io.github.sabas0ba.sabashader/Modules/<名前>/
<名前>.scmodule モジュール定義(JSON)
properties.hlsl プロパティ定義
includes.hlsl Core の include
phase_<フェーズ>.hlsl 差し込まれるコード
<名前>Core.hlsl 数式(Unity 非依存・テスト対象)
lang/ja-JP.po, en-US.pophase_<フェーズ名>.hlsl は JSON に書かなくても自動で拾われます。 順序の指定が要るときだけ JSON に phases を書きます。
順序を指定するときはファイル名を変える
JSON に phases を書くと、そのフェーズのファイル名を phase_*.hlsl から 外す必要があります。Shader Core は JSON のフェーズを読んだあと、 ディレクトリの phase_*.hlsl を無条件に足すためです (SCModule.FromFile の AddRange。重複を除くための exists は 組み立てられるだけで使われていません)。両方に該当すると同じコードが 2 回差し込まれ、効果が二重にかかります。
同梱の CrtGlitch は、ドット絵風の後にかけたいので JSON 側だけに載せています。
{
"name": "__CrtGlitch",
"uniqueID": "io.github.sabas0ba.crtglitch",
"phases": [
{ "phase": "postpixel", "path": "crt_postpixel.hlsl", "afters": ["__PixelArt"] }
]
}afters / befores に書いた名前のモジュールが無い場合は無視されるだけなので、 同梱していないモジュールを指しても壊れません。順序を指定しなければ モジュール名の辞書順になります。
この食い違いはハーネス側では見えません(ハーネスは重複を除きます)。 tests/test_scmodule.py::test_package_module_phase_files_are_declared_once が 両方に該当する状態を落とします。
フェーズ
| フェーズ | いつ | 何ができるか |
|---|---|---|
morph | 頂点の最初 | 変形・法線操作 |
postvertex | 頂点の最後 | クリップ空間での操作 |
base | ベースカラーとノーマル取得後 | 色調補正、デカール、UV 操作 |
light | ライトごと | スペキュラの加算 |
customlight | ライト合成前 | 独自ライトの追加 |
modifylight | 全ライト合成後 | ライト色・方向の変更、独自の影 |
shade | 乗算シェーディング | |
reflection | 任意の合成 | 多くの場合、順序の指定が要る |
add | 加算シェーディング | リムライト、スペキュラ |
postpixel | ピクセルの最後 | 仕上げの色操作 |
base フェーズの時点では sd.N はまだ接空間です。ワールド空間の法線が 要るときは vertex.N を使い、ワールドのベクトルを接空間へ持ち込むときは mul(vertex.TBN, v) とします。
プロパティ名は書き換わる
モジュールのプロパティ名には uniqueID が前置きされます。 io.github.example.foo の _Amount は _io_github_example_foo_Amount になります。 モジュールの HLSL は素の名前で書き、インポータが書き換えます。
このため、サンプラーを自前で宣言してはいけません。sampler_Texture が _io_github_example_foosampler_Texture になり、Unity のインライン sampler の 命名規約から外れます。Shader Core が用意している SCSampleRepeat / SCSampleClamp、または sampler_linear_repeat を使ってください。
数式はテストできる形に切り出す
Unity 依存のない数式は <名前>Core.hlsl に出します。 docs/testing.md の制約(全成分を書く、行列・テクスチャ・ グローバル変数を使わない、prelude にある組み込みだけ)を守れば、 ヘッドレス描画の回帰テストにそのままかけられます。
時間に依存するものは、_Time を直接読まずに引数で受け取ってください。 テストから固定値を渡せて、描画が決定的になります。
有効化
Shader Core はシェーダーごとに有効なモジュールを持ちます。既定値は 「そのシェーダーと同じディレクトリにあるモジュール」なので、別ディレクトリに 置いた本パッケージのモジュールは明示的に有効化が要ります。
Unity では、シェーダーのインスペクタに出るモジュール一覧で切り替えて Apply を押します。設定は ProjectSettings/jp.lilxyzw.shadercore.asset に 保存されます。
検証用プロジェクトでは tools/setup_unity_project.py がこの設定を書き出し、 パッケージ内のモジュールを Illust2D で有効にします。Thin2D や Debug は同じ phase・変数を公開していないため、一律には追加しません。NonToon では検証済みの Mochi Skin と Transformation Bank を有効にします。
テスト
tools/dev.sh python -m pytest tests -qtests/test_scmodule.py が次を見ます。
uniqueIDの欠落、フェーズのファイルの欠落- プロパティ名が
uniqueIDで書き換わっているか - 同一フェーズ内の
befores/aftersの矛盾と循環 - モジュールが宣言していないプロパティを参照していないか
lang/*.poの翻訳漏れ
頂点変位の使いどころ
morph フェーズで頂点を動かせますが、縁を丸めることはできません。 丸めるにはジオメトリを足す必要があり、モジュールはパスを追加できず テッセレーションのフックもありません。
そのため厚みを出す表現(積もりなど)は、次のような前提で使ってください。
- 真横から縁を見る場面を想定しない。上から見る面や遠景に向く
- 縁をなだらかにしたい場合は、頂点カラーで厚みを落として境界をぼかす
- 硬いエッジのメッシュ(立方体など)は、面ごとに頂点が分かれているため 面の向きで厚みが変わると継ぎ目が開く
近景で縁まで作り込む必要がある場合は、モジュールではなくシェーダー本体を 作り、シェル用のパスを持たせる方が向いています。
VRChat での制約
VRChat は Android / Quest のアバターに SDK 付属シェーダーしか許可しません。 このパッケージのシェーダーとモジュールは PC アバターとワールド専用です。