MMEからの変換
MMEと異なる部分は、主に以下の3点です。
・スキニング
・ライト処理
・セルフシャドウ
MMEのエフェクトをMMMで使うと、モデルの動きが反映されない、全体的に暗くなるといった現象がある場合は、
これらの処理がMMEと異なることが原因です。
具体的な使用方法については、MMMに同梱のSampleBase.fxm を参考にしてください。
スキニング
スキニングとは、ボーンによるモデル変形のことです。
このスキニング処理を、MMDではCPUで、MMMではGPUで実行しています。
・MMD/MMEでは、ボーンの動きが反映された状態の頂点情報がシェーダに渡されます。
・MMMでは、初期位置の頂点情報がシェーダに渡され、シェーダでスキニング処理が行われます。
従って、MMMのエフェクトでは、モデルを描画する部分について変更する必要があります。
変換方法は、基本的にMMEエフェクトで下記のように頂点シェーダ内で座標変換を行っている部分を、
VS_OUTPUT Basic_VS(float4 Pos : POSITION, float3 Normal : NORMAL, float2 Tex : TEXCOORD0)
{
...
Out.Pos = mul(Pos, WorldViewProjMatrix);
Out.Normal = mul(Normal, WorldViewProjMatrix);
以下のように変更します。
VS_OUTPUT Basic_VS(MMM_SKINNING_INPUT IN)
{
...
MMM_SKINNING_OUTPUT SkinOut = MMM_SkinnedPositionNormal(IN.Pos, IN.Normal, IN.BlendWeight, IN.BlendIndices, IN.SdefC, IN.SdefR0, IN.SdefR1);
Out.Pos = mul(SkinOut.Position, WorldViewProjMatrix);
Out.Normal= mul(SkinOut.Normal, WorldViewProjMatrix);
ライト処理
MMMでは、3つのライトを使うことができます。
3つのライトに対応するには、シェーダ内のライト色に関連する部分を変更する必要があります。
ただし、1つのライトのみ対応でよい場合は、特に変更する必要はありません。
3つのライトを使用する場合は、主に以下の値を利用します。
bool LightEnables[MMM_LightCount] : LIGHTENABLES;
float3 LightDiffuses[MMM_LightCount] : LIGHTDIFFUSECOLORS;
float3 LightAmbients[MMM_LightCount] : LIGHTAMBIENTCOLORS;
float3 LightSpeculars[MMM_LightCount] : LIGHTSPECULARCOLORS;
LIGHTENABLES は、ライトが有効になっているかどうかの配列、
その他3つはライトのDiffuse、Ambient、Specular色の配列です。
これらを利用して、色を計算します。
基本的に、これらを利用したベース色の計算は決まっており、比較的簡単に置き換えが可能です。
■ まず、頂点シェーダの頂点色の計算を以下のように行います。
ただ、これはあくまでデフォルトの計算と同じですので注意してください。
...
static float4 DiffuseColor[3] = { MaterialDiffuse * float4(LightDiffuses[0], 1.0f)
, MaterialDiffuse * float4(LightDiffuses[1], 1.0f)
, MaterialDiffuse * float4(LightDiffuses[2], 1.0f)};
static float3 AmbientColor[3] = { saturate(MaterialAmbient * LightAmbients[0]) + MaterialEmmisive
, saturate(MaterialAmbient * LightAmbients[1]) + MaterialEmmisive
, saturate(MaterialAmbient * LightAmbients[2]) + MaterialEmmisive};
static float3 SpecularColor[3] = { MaterialSpecular * LightSpeculars[0]
, MaterialSpecular * LightSpeculars[1]
, MaterialSpecular * LightSpeculars[2]};
...
VS_OUTPUT Basic_VS(MMM_SKINNING_INPUT IN)
{
...
float3 color = float3(0, 0, 0);
float3 ambient = float3(0, 0, 0);
float count = 0;
for (int i = 0; i < 3; i++) {
if (LightEnables[i]) {
color += (float3(1,1,1) - color) * (max(0, DiffuseColor[i] * dot(Out.Normal, -LightDirection[i])));
ambient += AmbientColor[i];
count = count + 1.0;
}
}
Out.Color.rgb = saturate(ambient / count + color);
Out.Color.a = MaterialDiffuse.a;
...
}
■ 次に、スペキュラ色の計算をピクセルシェーダで行います。
float4 Basic_PS(VS_OUTPUT IN)
{
...
float3 HalfVector;
float3 Specular = 0;
for (int i = 0; i < 3; i++) {
if (LightEnables[i]) {
HalfVector = normalize( normalize(IN.Eye) + -LightDirection[i] );
Specular += pow( max(0,dot( HalfVector, normalize(IN.Normal) )), SpecularPower ) * SpecularColor[i];
}
}
...
}
色々なシェーダが作られていますが、ほとんどのシェーダが同じ計算方法を使っています。
なので、これは頂点色、スペキュラ色の部分を置き換えれば大抵の場合OKです。
陰影とセルフシャドウ
MMDとMMMでは、セルフシャドウの処理が異なります。
MMDのmode1/mode2はMMMではありませんし、MMMでは16bit使用して深度を保存していたり、ソフトシャドウ機能があったりします。
MMMでは、陰影、セルフシャドウの処理については下記2つの関数を用意しています。
MMM_GetToonColor
MMM_GetSelfShadowToonColor
■ まず、セルフシャドウに必要な値を用意します。
float4x4 WorldMatrix : WORLD;
float4x4 LightWVPMatrices[MMM_LightCount] : LIGHTWVPMATRICES;
float3 LightDirection[MMM_LightCount] : LIGHTDIRECTIONS;
float3 LightPositions[MMM_LightCount] : LIGHTPOSITIONS;
float LightZFars[MMM_LightCount] : LIGHTZFARS;
float4 MaterialToon : TOONCOLOR;
■ 次に、頂点シェーダにセルフシャドウ計算に仕様する下記3つの項目を用意します。
struct VS_OUTPUT {
float4 Pos : POSITION; // 射影変換座標
float2 Tex : TEXCOORD0; // テクスチャ
float4 SubTex : TEXCOORD1; // サブテクスチャ/スフィアマップテクスチャ座標
float3 Normal : TEXCOORD2; // 法線
float3 Eye : TEXCOORD3; // カメラとの相対位置
float4 SS_UV1 : TEXCOORD4; // セルフシャドウテクスチャ座標
float4 SS_UV2 : TEXCOORD5; // セルフシャドウテクスチャ座標
float4 SS_UV3 : TEXCOORD6; // セルフシャドウテクスチャ座標
float4 Color : COLOR0; // ライト0による色
};
■ 頂点シェーダ内で、この3つの値を計算させます。SampleBase.fxmを参考にしてください。
VS_OUTPUT Basic_VS(MMM_SKINNING_INPUT IN, uniform bool useSelfShadow)
{
...
if (useSelfShadow) {
float4 dpos = mul(SkinOut.Position, WorldMatrix);
//デプスマップテクスチャ座標
Out.SS_UV1 = mul(dpos, LightWVPMatrices[0]);
Out.SS_UV2 = mul(dpos, LightWVPMatrices[1]);
Out.SS_UV3 = mul(dpos, LightWVPMatrices[2]);
Out.SS_UV1.y = -Out.SS_UV1.y;
Out.SS_UV2.y = -Out.SS_UV2.y;
Out.SS_UV3.y = -Out.SS_UV3.y;
Out.SS_UV1.z = (length(LightPositions[0] - SkinOut.Position) / LightZFars[0]);
Out.SS_UV2.z = (length(LightPositions[1] - SkinOut.Position) / LightZFars[1]);
Out.SS_UV3.z = (length(LightPositions[2] - SkinOut.Position) / LightZFars[2]);
}
...
}
■ ピクセルシェーダ内で、関数を使って色を計算します。
条件式 (!useSelfShadow && useToon && usetoontexturemap ) 以下が「モデルかつトゥーンを利用している場合」
条件式 (useToon && usetoontexturemap) 以下が「セルフシャドウかつトゥーンも利用」
その他 (else) が「セルフシャドウ利用、トゥーン色なし」
です。
float4 Basic_PS(VS_OUTPUT IN, uniform bool useToon, uniform bool useSelfShadow) : COLOR0
{
...
float3 color;
if (!useSelfShadow && useToon && usetoontexturemap ) {
color = MMM_GetToonColor(MaterialToon, IN.Normal, LightDirection[0], LightDirection[1], LightDirection[2]);
Color.rgb *= color;
}
if (useSelfShadow) {
if (useToon && usetoontexturemap) {
color = MMM_GetToonColor(MaterialToon, IN.Normal, LightDirection[0], LightDirection[1], LightDirection[2]);
float3 shadow = MMM_GetSelfShadowToonColor(MaterialToon, IN.Normal, IN.SS_UV1, IN.SS_UV2, IN.SS_UV3, false, useToon);
Color.rgb *= min(shadow, color);
}
else {
Color.rgb *= MMM_GetSelfShadowToonColor(MaterialToon, IN.Normal, IN.SS_UV1, IN.SS_UV2, IN.SS_UV3, false, useToon);
}
}
...
}
これを、陰影(トゥーン)やセルフシャドウを記述している部分と置き換えます。
full.fxをそのまま利用しているもの、独自にソフトシャドウを記述しているものなどケースは様々ですので、一概にここ、とは言えません。