![プログラミング プログラミング](https://srad.jp/static/topics/programming_64.png)
Ledの日記: MME/MMMエフェクト関連の詳細まとめ
MME用の.fxファイルを自動変換してMMM対応するプログラムを書いていたら、いろいろ不思議な仕様やバグと思われるものまで出てきた。ソースコード中にコメントで書いていたりするが、MMM用にエフェクトを書きたい人のためにメモしておく。(作者のMoggさんは最近多忙らしくMMMも1年以上更新されていない。知らせたとしてすぐに更新されるわけでもないと思われるので、いろいろ洗い出してから知らせることにする。)
-------------------
MMEの仕様で注意すること
●float4 EgColorとfloat4 SpcColor
この二つの変数はREFERENCE.txt(マニュアル)にないが、古いMMEで材質モーフ後のエッジ色、反射色と反射強度を取得するのに使われていて、この仕様はMME v0.37でもまだ生きている。MMMでは材質モーフ後の色しか取得しないので、MMMのマニュアルに従って変数を設定すれば移植できる。
--------------------
MMM用のエフェクトを書く際に注意すること
●ライト関連のジオメトリ変換行列の制約
MMMのエフェクト開発マニュアル(この執筆時点でv1系はv1.2.7.2が最新)にはライト関連の座標変換行列を取得するには
float4x4 LightWorldViewProjMatrix : WORLDVIEWPROJECTION < string Object="Light";>;
のように書いても取得できるとの記述がある。が、実際やってみるとどうやら< string Object="Light";> のアノテーション部分が無視されているようだ。(アノテーションがあってもなくても同じ動作に見える。) ワークアラウンドとして、最もよく使うライトのWORLDVIEWPROJECTIONに関しては代わりにLIGHTWVPMATRICESというセマンティクスが用意されている。これを使って
float4x4 LightWorldViewProjMatrices[MMM_LightCount] : LIGHTWVPMATRICES;
#define LightWorldViewProjMatrix LightWorldViewProjMatrices[0]
などと書くべきであるようだ。
なお、こうやって計算したLightWorldViewProjMatrixでもz座標の計算が不安定である。OFFSCREENRENDERTARGETの描画で、この行列を掛け算した位置ベクトルのz座標を用いて描画を行うとひどくチラついてしまう。(MMMのUIから当該OFFSCREENRENDERTARGETを表示すると綺麗に表示されるので中々気づけない) なお、MMM添付のSampleBase.fxmでは、z座標だけ別に計算しなおしている。
また、
float4x4 LightWorldMatrix: WORLD < string Object="Light"; >;
float4x4 LightViewMatrix : VIEW < string Object ="Light"; >;
float4x4 LightProjMatrix : PROJECTION < string Object ="Light"; >;
これらの記述も同様にアノテーションを無視したような動作をする。
●DefaultEffectで none, hideばかりのOFFSCREENRENDERTARGETがUIから省略される
デフォルトで指定するシェーダが無いOFFSCREENRENDERTARGETはMMMのUIからアクセス不能になってしまう。エフェクト作者の立場からこれを防ぐには、DefaultEffectに
self=dummy.fxm
のような指定を追加して、さらに何も描画しないエフェクトdummy.fxmを作成しておく。なお、UIにはデフォルト指定されたファイル名が表示されるので、OFFSCREENRENDERTARGETの名前を(dummyの代わりに)エフェクトのファイル名にすると使いやすくなるだろう。
(なお、MMEではselfは「このエフェクトが割り当てられたモデル」だが、MMMでは必ずポリゴンを持たない「エフェクトオブジェクトと呼んで良さそうな何か」になる。selfに何を指定してもMMMでは描画に影響ない、はず。)
●エッジ色のα値について
MMM添付のSampleBase.fxmではエッジ用のtechniqueで
AlphaBlendEnable = FALSE;
AlphaTestEnable = FALSE;
という指定がされているが、AlphaTestEnable=TRUEに書き換えてやって、ピクセルシェーダでα値を0にすると描画をストップできるようだ。ピクセルシェーダ側でポリゴンに穴をあけたい場合などに使える。なお、AlphaBlendEnable=TRUEとしても無視されるようだ。
MMEではエッジであっても透過処理をしてくれるので、ここも差異として記録しておく。
●UseToonおよびuse_toonの動作の差異
MMEとMMMではトゥーン指定がない材質の描画でUseToonおよびuse_toonの真偽が異なるようだ。このせいでMME用のエフェクトをMMMに持ってくるとモデルの表示が醜くなってしまうことがある。回避するには、まずMMMの特殊パラメータ
bool usetoontexturemap; // Toonテクスチャフラグ
をエフェクトの先頭付近に追加しておいて、その後MMDPass="object"のピクセルシェーダでUseToonの値が入っている変数(含use_toon)を
(use_toon && usetoontexturemap)
のように書き直す。
●シャドウバッファ
MMEではセルフシャドウに使うバッファにアクセスする際に
sampler DefSamp : register(s0);
と書く約束になっている(DefSampは別の名前でもよい)。が、MMMでは違うものを使う。MMMではMMM_GetSelfShadowToonColorとMMM_GetToonColorの2つの関数を使ってセルフシャドウの計算をする方法が推奨されているが、それでは自動変換が大変やりづらい。
代替案を探すためMMMをよく観察した。MMMではデフォルトでMMM_SelfShadowDepthMap1というテクスチャが定義されていて、これにアクセスするとMMDのセルフシャドウに似た計算ができる。ただし、計算値が違うため換算処理を書いてやらなくてはならない。MMDのデフォルトシェーダにとても近い描画をするMMEエフェクトのfull.fxにて、シャドウバッファ座標のz座標を
Out.ZCalcTex.z = (length(LightPosition - SkinOut.Position) / LightZFar);
に修正する必要がある。また、SKIIの値を5倍程度にしなければ、モデルとカメラの距離が離れるとすぐに影が薄く消えてしまう。
-----------------------
(2017_06_09追記)
float4 EdgeColorという変数をグローバルで定義するとMMEでもMMMでもエッジの色を取得できるが、MMMではその非透明度が1ではなく2程度になっている。対策としてEdgeColorを参照しているところを見つけたら全部 saturate(EdgeColor)に修正する。
-----------------------
(2017_07_04追記)
実はHLSLのテクスチャサイズには限界があった。MMEでは限界を超えたサイズのテクスチャサイズを適当にごまかすようだが、MMMではエラーを吐いて落ちる。なお、MMMではテクスチャサイズの限界は1辺8192ピクセルのようだ。
----------------------
他にもまだ出てくると思われるが、気が付いたときに書き足す。
MME/MMMエフェクト関連の詳細まとめ More ログイン