マテリアルビルダー
MMDモデルを読み込む過程において、テクスチャの読み込みとマテリアルの割り当ての作業は、完全にマテリアルビルダーに委任されています。
PmxLoaderとPmdLoaderのmaterialBuilderはローダーオプションを通じて設定できます。
以下の理由からマテリアルの読み込み処理を分離しました:
-
測定によると、平均的に、MMDモデルの読み込みで最も時間がかかる部分はテクスチャを読み込んでマテリアルを初期化することです。しかし、ユースケースによっては、マテリアルの読み込みが必要ない場合があります。そのため、ユーザーが選択的にマテリアルを読み込み、読み込み時間を短縮できるようにする必要があります。
-
さらに、ユーザーは読み込み時にMMDのシェーディングモデルではなく、異なるマテリアルに基づいたシェーディングを適用できるべきです。例えば、よりリアルな見た目のために物理ベースレンダリング(PBR)マテリアルを使用したい場合があります。
マテリアルビルダーの紹介
すべてのMMDモデルローダーでは、loaderOptions.mmdmodel.materialBuilder: Nullable<IMaterialBuilder> を通じてマテリアルビルダーを設定できます。
このオプションのデフォルトは**null** ですが、babylon-mmdのインデックスをインポートすると、MmdStandardMaterialBuilderがデフォルトとして設定されます。
この文脈でインデックスをインポートするとは、このようなコードを意味します:
import { Something } from "babylon-mmd";
逆に、シンボルが定義されているファイルを直接インポートすることもできます:
import { Something } from "babylon-mmd/esm/something";
ツリーシェイキングの目的で、loaderOptions.mmdmodel.materialBuilderのデフォルト値はnullに設定されています。
しかし、初心者がライブラリを使いやすくするために、インデックスをインポートするとMmdStandardMaterialBuilderがデフォルトとして設定されるように設計されています。
マテリアルビルダーは次のように設定できます:
const assetContainer: AssetContainer = await LoadAssetContainerAsync(modelFileOrUrl, scene, {
pluginOptions: {
mmdmodel: {
materialBuilder: new MmdStandardMaterialBuilder()
}
}
});
単一のマテリアルで複数のモデルを読み込む場合は、マテリアルビルダーを共有できます。
この場合、マテリアルビルダーは内部でテクスチャをキャッシュするため、特に同じモデルを複数回読み込む場合、読み込み時間を大幅に短縮できます。
const pbrMaterialBuilder = new PBRMaterialBuilder();
const assetContainer1: AssetContainer = await LoadAssetContainerAsync(modelFileOrUrl1, scene, {
pluginOptions: {
mmdmodel: {
materialBuilder: pbrMaterialBuilder
}
}
});
const assetContainer2: AssetContainer = await LoadAssetContainerAsync(modelFileOrUrl2, scene, {
pluginOptions: {
mmdmodel: {
materialBuilder: pbrMaterialBuilder
}
}
});
マテリアルビルダーの種類
IMmdMaterialBuilder インターフェースを実装することでマテリアルビルダーを作成できます。また、babylon-mmdが提供するマテリアルビルダーもこのインターフェースを実装しています。
babylon-mmdは3つのマテリアルビルダーを提供しています:
MmdStandardMaterialBuilder-MmdStandardMaterialを使用してMMDの動作を再現するマテリアルビルダー。StandardMaterialBuilder- Babylon.jsの**StandardMaterial** を使用するマテリアルビルダー。PBRMaterialBuilder- Babylon.jsの**PBRMaterial** を使用するマテリアルビルダー。
MmdStandardMaterialBuilder
MmdStandardMaterialBuilder は**MmdStandardMaterial** を使用してMMDマテリアルを読み込むマテリアルビルダーです。
このマテリアルビルダーはbabylon-mmdがサポートするすべてのMMDマテリアルプロパティを読み込み、各カテゴリ用のメソッドが提供されています。
読み込み動作を変更したい場合は、_setMeshesAlphaIndexを除いて、対応するメソッドをオーバーライドできます。
MmdStandardMaterialBuilder._setMeshesAlphaIndexによって設定されるプロパティ:
AbstractMesh.alphaIndex- マテリアルの順序に従ってマテリアルのアルファインデックスを設定します(下記のレンダーメソッドを参照)。
MmdStandardMaterialBuilder.loadGeneralScalarPropertiesによって設定されるプロパティ:
StandardMaterial.diffuseColor- MMDマテリアルの「拡散色」(rgb)StandardMaterial.specularColor- MMDマテリアルの「光沢色」(rgb)StandardMaterial.ambientColor- MMDマテリアルの「環境色」(rgb)Material.alpha- MMDマテリアルの「拡散色」(a)AbstractMesh.isVisible- 「拡散色」(a)が0の場合はfalseに設定StandardMaterial.specularPower- MMDマテリアルの「反射率」
MmdStandardMaterialBuilder.loadDiffuseTextureによって設定されるプロパティ:
Material.backFaceCulling- MMDマテリアルの「両面描画」StandardMaterial.diffuseTexture- MMDマテリアルの「テクスチャ」
MmdStandardMaterialBuilder.setAlphaBlendModeによって設定されるプロパティ:
StandardMaterial.diffuseTexture.hasAlpha- MMDマテリアルの「テクスチャ」がアルファチャンネルを持つ場合はtrueに設定(下記のアルファ評価を参照)StandardMaterial.useAlphaFromDiffuseTexture- MMDマテリアルの「テクスチャ」がアルファチャンネルを持つ場合はtrueに設定Material.transparencyMode- レンダーメソッドによって決定(下記のレンダーメソッドを参照)Material.forceDepthWrite- レンダーメソッドによって決定(下記のレンダーメソッドを参照)
MmdStandardMaterialBuilder.loadSphereTextureによって設定されるプロパティ:
MmdStandardMaterial.sphereTexture- MMDマテリアルの「スフィアテクスチャ」MmdStandardMaterial.sphereTextureBlendMode- MMDマテリアルの「スフィアテクスチャモード」
MmdStandardMaterialBuilder.loadToonTextureによって設定されるプロパティ:
MmdStandardMaterial.toonTexture- MMDマテリアルの「トゥーンテクスチャ」
MmdStandardMaterialBuilder.loadOutlineRenderingPropertiesによって設定されるプロパティ:
MmdStandardMaterial.renderOutline- アウトラインレンダリングを有効にするためにtrueに設定MmdStandardMaterial.outlineWidth- MMDマテリアルの「エッジサイズ」MmdStandardMaterial.outlineColor- MMDマテリアルの「エッジ色」(rgb)MmdStandardMaterial.outlineAlpha- MMDマテリアルの「エッジ色」(a)
StandardMaterialBuilder
StandardMaterialBuilder は**StandardMaterial** を使用してMMDマテリアルを読み込むマテリアルビルダーです。
このマテリアルビルダーはMMDマテリアルプロパティのサブセットのみを読み込むため、読み込み処理中にデータの損失が発生します。
読み込み動作を変更したい場合は、_setMeshesAlphaIndexを除いて、対応するメソッドをオーバーライドできます。
StandardMaterialBuilder._setMeshesAlphaIndexによって設定されるプロパティ:
AbstractMesh.alphaIndex- マテリアルの順序に従ってマテリアルのアルファインデックスを設定します(下記のレンダーメソッドを参照)。
StandardMaterialBuilder.loadGeneralScalarPropertiesによって設定されるプロパティ:
StandardMaterial.diffuseColor- MMDマテリアルの「拡散色」(rgb)StandardMaterial.specularColor- MMDマテリアルの「光沢色」(rgb)StandardMaterial.ambientColor- MMDマテリアルの「環境色」(rgb)Material.alpha- MMDマテリアルの「拡散色」(a)AbstractMesh.isVisible- 「拡散色」(a)が0の場合はfalseに設定StandardMaterial.specularPower- MMDマテリアルの「反射率」
StandardMaterialBuilder.loadDiffuseTextureによって設定されるプロパティ:
Material.backFaceCulling- MMDマテリアルの「両面描画」StandardMaterial.diffuseTexture- MMDマテリアルの「テクスチャ」
StandardMaterialBuilder.setAlphaBlendModeによって設定されるプロパティ:
StandardMaterial.diffuseTexture.hasAlpha- MMDマテリアルの「テクスチャ」がアルファチャンネルを持つ場合はtrueに設定(下記のアルファ評価を参照)StandardMaterial.useAlphaFromDiffuseTexture- MMDマテリアルの「テクスチャ」がアルファチャンネルを持つ場合はtrueに設定Material.transparencyMode- レンダーメソッドによって決定(下記のレンダーメソッドを参照)Material.forceDepthWrite- レンダーメソッドによって決定(下記のレンダーメソッドを参照)
以下の3つのメソッドは空であり、オーバーライドすることで任意に実装できます:
StandardMaterialBuilder.loadSphereTextureStandardMaterialBuilder.loadToonTextureStandardMaterialBuilder.loadOutlineRenderingProperties
PBRMaterialBuilder
PBRMaterialBuilder は**PBRMaterial** を使用してMMDマテリアルを読み込むマテリアルビルダーです。
このマテリアルビルダーはMMDマテリアルプロパティのサブセットのみを読み込むため、読み込み処理中にデータの損失が発生します。 また、MMDマテリアルパラメータと1対1のマッピングがないプロパティについては、追加の変換によるデータの歪みが発生する可能性があります。
読み込み動作を変更したい場合は、_setMeshesAlphaIndexを除いて、対応するメソッドをオーバーライドできます。
PBRMaterialBuilder._setMeshesAlphaIndexによって設定されるプロパティ:
AbstractMesh.alphaIndex- マテリアルの順序に従ってマテリアルのアルファインデックスを設定します(下記のレンダーメソッドを参照)。
PBRMaterialBuilder.loadGeneralScalarPropertiesによって設定されるプロパティ:
PBRMaterial.albedoColor- MMDマテリアルの「拡散色」(rgb)PBRMaterial.reflectionColor- MMDマテリアルの「光沢色」(rgb)PBRMaterial.ambientColor- MMDマテリアルの「環境色」(rgb)Material.alpha- MMDマテリアルの「拡散色」(a)AbstractMesh.isVisible- 「拡散色」(a)が0の場合はfalseに設定PBRMaterial.roughness- MMDマテリアルの「反射率」
PBRMaterialBuilder.loadDiffuseTextureによって設定されるプロパティ:
Material.backFaceCulling- MMDマテリアルの「両面描画」PBRMaterial.albedoTexture- MMDマテリアルの「テクスチャ」
PBRMaterialBuilder.setAlphaBlendModeによって設定されるプロパティ:
PBRMaterial.albedoTexture.hasAlpha- MMDマテリアルの「テクスチャ」がアルファチャンネルを持つ場合はtrueに設定(下記のアルファ評価を参照)PBRMaterial.useAlphaFromAlbedoTexture- MMDマテリアルの「テクスチャ」がアルファチャンネルを持つ場合はtrueに設定Material.transparencyMode- レンダーメソッドによって決定(下記のレンダーメソッドを参照)Material.forceDepthWrite- レンダーメソッドによって決定(下記のレンダーメソッドを参照)
以下の3つのメソッドは空であり、オーバーライドすることで任意に実装できます:
PBRMaterialBuilder.loadSphereTexturePBRMaterialBuilder.loadToonTexturePBRMaterialBuilder.loadOutlineRenderingProperties
レンダーメソッド
MMDはデプスライトとデプステストを有効にしたアルファブレンディングを使用してメッシュをレンダリングします。 マテリアルビルダーは、この動作を実装しながら最適化された結果を得るためのいくつかのオプションを提供します。
メッシュが完全に不透明である場合、アルファブレンディングなしでレンダリングしても同じ結果が得られます。babylon-mmdはレンダリングの最適化のためにこれを自動的に実行するためのいくつかのオプションを提供しており、それはマテリアルビルダーのrenderMethodによって制御されます。
DepthWriteAlphaBlendingWithEvaluation
このレンダリングメソッドは不透明なメッシュをアルファブレンディングなしでレンダリングし、絶対に必要な場合にのみアルファブレンディングを使用します。
つまり、このメソッドでモデルを読み込むと、マテリアルのtransparencyModeは**Material.MATERIAL_ALPHABLEND** または**Material.MATERIAL_OPAQUE** のいずれかになり、forceDepthWriteは**true** に設定されます。
これがデフォルトのメソッドです。
DepthWriteAlphaBlending
このレンダリングメソッドはすべてのメッシュをアルファブレンディングを使用してレンダリングします。
つまり、このメソッドでモデルを読み込むと、マテリアルのtransparencyModeは常に**Material.MATERIAL_ALPHABLEND** であり、forceDepthWriteは**true** に設定されます。
このメソッドはMMDのレンダリングメソッドと同一なので、レンダリングの問題が発生した場合は、このメソッドを試すことをお勧めします。
AlphaEvaluation
このレンダリングメソッドは、メッシュをアルファブレンディング、アルファテスト、または不透明モードのいずれでレンダリングするかを決定し、アルファブレンディングを使用する場合にはデプスライトを実行しません。
つまり、このメソッドでモデルを読み込むと、マテリアルのtransparencyModeは**Material.MATERIAL_ALPHATEST、Material.MATERIAL_ALPHABLEND、またはMaterial.MATERIAL_OPAQUE** のいずれかになり、forceDepthWriteは**false** に設定されます。
このメソッドはBabylon.jsのレンダリングパイプラインと最も互換性がある方法です。デプスライトとアルファブレンドの併用は一般的な手法ではないためです。
アルファ評価
上記のレンダリングメソッドのうち、MmdMaterialRenderMethod.DepthWriteAlphaBlendingWithEvaluation はメッシュが不透明かどうかを判断する必要があります。また、MmdMaterialRenderMethod.AlphaEvaluation はメッシュのアルファ値を評価して適切なレンダリングメソッドを選択する必要があります。
このプロセスをアルファ評価と呼びます。
プロセス
- ジオメトリをUVスペースでレンダーターゲットにレンダリングします。この時、テクスチャをサンプリングして各ピクセルのアルファ値のみをレンダリングします。
- readPixels関数を使用してレンダーターゲットのピクセルデータを読み取ります。
- 読み取ったピクセルデータからアルファ値を評価して、適切なレンダリングメソッドを選択します。
MmdMaterialRenderMethod.DepthWriteAlphaBlendingWithEvaluationの場合、テクスチャ付きのジオメトリのフラグメントが1つでも255以外のアルファ値を持つ場合、マテリアルのtransparencyModeは**Material.MATERIAL_ALPHABLEND** に設定されます。MmdMaterialRenderMethod.AlphaEvaluationの場合、マテリアルのレンダリングメソッドはマテリアルビルダーの**alphaThreshold** と**alphaBlendThreshold** の値によって決定されます。
注意点
アルファ評価は一部のエッジケースで正しく機能しない場合があります。例えば、メッシュのUVトポロジーが異常な場合、アルファ評価は不正確な結果を生成する可能性があります。この場合、マテリアルビルダーの**alphaEvaluationResolution** を増やすことで問題が解決する場合があります。
アルファ評価を実行する場合、すべてのマテリアルは読み込み時に一度レンダーターゲットにレンダリングされる必要があります。これは無視できないコストです。そのため、マテリアルビルダーの**forceDisableAlphaEvaluation** オプションを使用してアルファ評価を無効にすることができます。
この場合、アルファ評価は実行されません。
また、BPMXフォーマットはアルファ評価の結果をフォーマット内に格納するため、それを使用して読み込み時のアルファ評価プロセスをスキップすることができます。
描画順序の設定
MMDは常にマテリアルの順序に従ってメッシュをレンダリングします。 しかし、Babylon.jsはレンダリング前にカメラからの距離に基づいてメッシュをソートします。
babylon-mmdはMMDと同じ描画順序を再現するために、2つのケースに対して別々のソリューションを提供します。
描画順序の再現は、renderMethodがMmdMaterialRenderMethod.AlphaEvaluationの場合には適用されないことに注意してください。
複数のメッシュの処理
MMDの描画順序は、Mesh.alphaIndex に適切な値を設定することで再現されます。
マテリアルビルダーの以下の2つのプロパティがこのために使用されます:
nextStartingAlphaIndex- 次のMMDモデルの開始アルファインデックス値alphaIndexIncrementsPerModel- 各MMDモデルのアルファインデックスの増分値
nextStartingAlphaIndex は1つのMMDモデルを読み込んだ後に**alphaIndexIncrementsPerModel** だけ増加します。
したがって、以下の設定で:
nextStartingAlphaIndex: 0alphaIndexIncrementsPerModel: 3
マテリアルが2つあるMMDモデルAとマテリアルが3つあるMMDモデルBを順番に読み込むと、nextStartingAlphaIndex は以下のように変化します:
- 読み込み前、
nextStartingAlphaIndex: 0 - モデルA読み込み後、
nextStartingAlphaIndex: 3 - モデルB読み込み後、
nextStartingAlphaIndex: 6
そして、読み込まれたモデルの**Mesh.alphaIndex** は以下のように設定されます:
Model A: {
Mesh1: { alphaIndex: 0 }
Mesh2: { alphaIndex: 1 }
}
Model B: {
Mesh1: { alphaIndex: 3 }
Mesh2: { alphaIndex: 4 }
Mesh3: { alphaIndex: 5 }
}
ここで重要なのは、alphaIndexIncrementsPerModelが十分に大きくない場合、以前に読み込まれたモデルと新しく読み込まれたモデルの**Mesh.alphaIndex** が重複する可能性があることです。
例えば、前の例で**alphaIndexIncrementsPerModel** が1に設定されていた場合、各モデルの**Mesh.alphaIndex** は以下のようになります:
Model A: {
Mesh1: { alphaIndex: 0 }
Mesh2: { alphaIndex: 1 }
}
Model B: {
Mesh1: { alphaIndex: 1 }
Mesh2: { alphaIndex: 2 }
Mesh3: { alphaIndex: 3 }
}
モデルAのMesh2とモデルBのMesh1は同じ**alphaIndex** を持つため、それらの描画順序はカメラからの距離によって決定されます。
この問題を防ぐために、alphaIndexIncrementsPerModel のデフォルト値は十分に大きな数に設定されています。
この方法を使用する場合、描画順序はMMDモデルが読み込まれる順序によって決まることに注意してください。
MMDモデル間の描画順序を厳密に再現する必要がある場合は、alphaIndexIncrementsPerModelを0に設定し、Mesh.alphaIndexを手動で調整することができます。
複数のサブメッシュの処理
Babylon.jsでは、単一メッシュ内の複数のサブメッシュ間の描画順序を制御する方法はありません。
単一のメッシュが複数のサブメッシュを持つ場合、描画順序は各サブメッシュ自身の境界球の中心に基づいてカメラからの距離を計算することによって決定されます。
この動作を考慮して、babylon-mmdはMMDモデルを読み込む際にすべてのサブメッシュに同じBoundingInfoを適用します。
この場合、すべてのサブメッシュはカメラからの距離が同じになり、安定したソートにより**Mesh.subMeshes** の順序で描画されます。
これはMMDモデルローダーの**loaderOptions.mmdmodel.optimizeSubmeshes** オプションが**false** の場合、常に適用されます。