SDEFサポート
このセクションでは、球状変形(SDEF)を使用するMMDモデルを正しく読み込む方法について説明します。
SDEFとは何ですか?
一般的に、スキニングには**リニアブレンドスキニング(LBS)**が使用されます。
このメソッドは、複数のボーンが各頂点に影響を与える場合、各ボーンの影響の重みに基づいて線形補間によって頂点位置を決定します。
このメソッドはシンプルで高速ですが、キャンディラッパーアーティファクトのような問題を引き起こす可能性があります。
**球状変形(SDEF)**はこれらの問題を解決するために提案されたメソッドです。SDEFは頂点位置を決定する際に、線形補間の代わりに球面補間を使用し、頂点位置をより自然に補正します。
babylon-mmdにおけるSDEFサポート
多くのMMDモデルはSDEFを使用しています。対照的に、ほとんどの現代的なソフトウェアはLBSのみをサポートしています。
MMDモデルの仕様とMMDがSDEFをサポートしているため、babylon-mmdはMMDの動作と同じ結果を達成するためにSDEFをサポートしています。
Babylon.jsもSDEFをサポートしていないため、babylon-mmdは少し変わった方法を使用しています。
SDEFを処理するために4つのオプションが提供されています。
オプション1:LBSを使用する
SDEFスキニングを使用するモデルはLBSでレンダリングすることができます。
このメソッドは、Babylon.jsのデフォルトのスキニングメソッドをそのまま使用するため、互換性が最も高いです。
モデルを読み込む際に**pluginOptions.mmdmodel.useSdef
の値をfalse
に設定する**だけでLBSメソッドを使用できます。(デフォルト値はtrue
です。)
const assetContainer: AssetContainer = await LoadAssetContainerAsync(
modelFileOrUrl,
scene,
{
pluginOptions: {
mmdmodel: {
useSdef: false
}
}
}
);
ただし、以下のようなアーティファクトが発生する可能性があります。
この動画はSDEFを使用するMMDモデルをLBSでレンダリングした結果を示しています。モデル:YYB式初音ミク_10th_v1.02(制作者:SANMUYYB)
オプション2:MmdStandardMaterialにのみSDEFを適用する
MmdStandardMaterial
を使用してレンダリングする場合、SDEFを適用できます。
MmdStandardMaterial
はbabylon-mmdが提供するマテリアルで、MMDモデルをレンダリングするために使用されます。
このマテリアルは、MaterialPlugin
を使用してBabylon.jsのStandardMaterial
を修正して作成されています。
MmdStandardMaterial
は様々なMMDマテリアルプロパティを提供し、SDEFをサポートしています。
このマテリアルを使用してMMDモデルを読み込み、SDEFを適用するには、以下のように読み込みます:
const assetContainer: AssetContainer = await LoadAssetContainerAsync(
modelFileOrUrl,
scene,
{
pluginOptions: {
mmdmodel: {
materialBuilder: new MmdStandardMaterialBuilder(),
useSdef: true
}
}
}
);
このマテリアルを使用するとSDEFが適用できますが、マテリアル以外のレンダーパイプラインの残りの部分には影響しません。
したがって、以下の動画に示すように、SDEFが適用されていても、シャドウマップには適用されず、代わりにLBSでレンダリングされた結果が表示されます。
この動画は*MmdStandardMaterial
を使用してSDEFが適用されたMMDモデルをレンダリングした結果**を示しています。モデル:YYB式初音ミク_10th_v1.02(制作者:SANMUYYB)*
オプション3:シェーダーインジェクションを使用してSDEFを適用する
このメソッドはBabylon.jsのシェーダーコンパイルのエントリーポイントを修正してSDEFを適用し、babylon-mmdが提供する最も強力なSDEFサポートメソッドです。
このメソッドはオプション2と一緒に使用するのが最適ですが、他のケースでもSDEFを適用できます。
Scene
を作成する前に、以下のコードを実行してEngine.createEffect
関数を修正します:
SdefInjector.OverrideEngineCreateEffect(engine);
これはプロトタイプを修正せず、Engine
インスタンスにcreateEffect関数を追加することで機能します。
この動画はシェーダーインジェクションを通じてSDEFを使用するMMDモデルをレンダリングした結果を示しています。モデル:YYB式初音ミク_10th_v1.02(制作者:SANMUYYB)
オプション4:CPUバウンドSDEFスキニング
このメソッドはCPUでSDEFスキニングを実行し、非常に低いパフォーマンスですが、どのレンダリングパイプラインでもSDEFを適用できます。
pluginOptions.mmdmodel.useSdef
をtrue
に設定し、すべてのメッシュの**computeBonesUsingShaders
の値をfalse
に設定**します。
const assetContainer: AssetContainer = await LoadAssetContainerAsync(
modelFileOrUrl,
scene,
{
pluginOptions: {
mmdmodel: {
useSdef: true
}
}
}
);
assetContainer.addAllToScene();
const modelMesh = assetContainer.meshes[0] as MmdMesh;
for (const mesh of modelMesh.metadata.meshes) mesh.computeBonesUsingShaders = false;
Babylon.jsのMesh
は、シェーダーでスキニングができない場合のフォールバックとしてCPUバウンドスキニングをサポートしています。ただし、Babylon.jsのCPUバウンドスキニングはSDEFをサポートしていないため、babylon-mmdはこれをオーバーライドしてSDEFをサポートしています。
useSdef
がtrue
に設定されている場合、CPUスキニングの実装がオーバーライドされ、SDEFサポートが必要なメッシュにはMesh
の代わりに**Mesh
を継承したSdefMesh
が使用されます**。
ただし、この場合、モーフターゲットを同時に使用することはできません。 これは、変形を適用する際、モーフターゲットはスキニングの前に適用されるべきですが、CPUバウンドでスキニングが先に処理される場合、モーフターゲットは後でシェーダーで適用されるため、変換順序が正しくなくなるためです。 したがって、ランタイムエラーは発生しませんが、非常に奇妙な結果を生み出す可能性があります。
SDEFシェーダーインジェクションの実装詳細
SDEFをサポートするためには、Babylon.jsの内部シェーダーコードを修正し、Babylon.js内のすべてのレンダーパイプラインを適切に修正する必要があります。
例えば、SDEFを使用するMMDモデルを被写界深度エフェクトで使用するには、深度レンダラーの頂点シェーダーを修正し、さらにレンダーパイプラインを修正してSDEFを適用するために必要な属性を追加でバインドする必要があります。
そのような作業は実質的に不可能なため、babylon-mmdは代わりにBabylon.jsのシェーダーコンパイルのエントリーポイントを修正してSDEFコードを挿入するメソッドを使用しています。
Babylon.jsはシェーダーを実行するために必要な属性、ユニフォームなどと一緒にシェーダーコードをEngine.createEffect
関数に渡します。babylon-mmdはこのプロセスを傍受してSDEF関連のコードを挿入します。
修正されたcreateEffect
関数は以下のプロセスを経ます:
- 現在コンパイルされているシェーダーがスケルトンをサポートしているかチェックする。
- スケルトンがサポートされている場合、SDEF用の属性を追加する。
- SDEFスキニングコードを挿入する。
- 修正されたパラメータを元の
createEffect
関数に渡す。
このアプローチの副作用として、スケルトンをサポートしているがSDEFをサポートしていないシェーダーにもSDEFコードが挿入されることです。
この場合、シェーダーの分岐発散により、LBSを使用するスケルトンをレンダリングする場合でも、SDEFを適用する場合と同じ計算コストが発生します。