XNA Game Studio 4.0のEffectインターフェース

 

XNAでモデル描画する場合は、一般的に以下のようなコードを書きます。

 Matrix[] transforms = new Matrix[model.Bones.Count];

model.CopyAbsoluteBoneTransformsTo(transforms);

foreach (ModelMesh mesh in model.Meshes)
{
    foreach (BasicEffect effect in mesh.Effects)
    {
        effect.World = transforms[mesh.ParentBone.Index] * world;
        effect.View = view;
        effect.Projection = projection;
    }

    mesh.Draw();
}

しかし、このコードのままだとモデルデータがBasicEffectを使っているときしか使えません。もしモデルデータがBasicEffect以外のエフェクトを使っている場合、「foreach (BasicEffect effect in mesh.Effects)」の部分で例外が発生します。この場合は以下のようなコードに変更します。

 foreach (ModelMesh mesh in model.Meshes)
{
    foreach (Effect effect in mesh.Effects)
    {
        if (effect is BasicEffect)
        {
            // 前と一緒
        }
        else
        {
            effect.Parameters["World"].SetValue(transforms[mesh.ParentBone.Index] * world);
            effect.Parameters["View"].SetValue(view);
            effect.Parameters["Projection"].SetValue(projection);
        }
    }

    mesh.Draw();
}

カスタムエフェクトのパラメーターをどのように設定するかはエフェクトによって異なります。多くの人達は上のコードのように命名規約を使ったり、他にはEffectAnnotationを使ってデータバインディングの仕組み作っていたりします。

私たちがSkinnedEffect、EnvironmentMapEffect、DualTextureEffectとAlphaTestEffectを追加したとき、モデル描画コードは汚くなってしまいました。それぞれのビルトイン・エフェクト毎に「if (effect is BasicEffect)」のようにエフェクトの型をチェックするコードを書き足す必要がありました。ぐはっ

この問題は以下の三つのインターフェースを追加することで解決しました。

 public interface IEffectMatrices
{
    Matrix World { get; set; }
    Matrix View { get; set; }
    Matrix Projection { get; set; }
}

public interface IEffectFog
{
    bool FogEnabled { get; set; }
    float FogStart { get; set; }
    float FogEnd { get; set; }
    Vector3 FogColor { get; set; }
}

public interface IEffectLights
{
    bool LightingEnabled { get; set; }
    Vector3 AmbientLightColor { get; set; }
    DirectionalLight DirectionalLight0 { get; }
    DirectionalLight DirectionalLight1 { get; }
    DirectionalLight DirectionalLight2 { get; }
    void EnableDefaultLighting();
}

すべてのビルトイン・エフェクトはIEffectMatrices、IEffectFogインターフェースを実装しています。また、BasicEffect、SkinnedEffect、そしてEnvironmentMapEffectはIEffectLightsインターフェースを実装しています。このことによって、ひとつのモデル内に型の違うビルトインエフェクトがが複数あっても以下のようにモデル描画を簡潔に記述することができます。

 Matrix[] transforms = new Matrix[model.Bones.Count];

model.CopyAbsoluteBoneTransformsTo(transforms);

foreach (ModelMesh mesh in model.Meshes)
{
    foreach (IEffectMatrices effect in mesh.Effects)
    {
        effect.World = transforms[mesh.ParentBone.Index] * world;
        effect.View = view;
        effect.Projection = projection;
    }

    mesh.Draw();
}

また、以下のヘルパーメソッドも追加しました。このメソッドは上の例と同じ動作をします。CopyAbsoluteBoneTransformsToやEffect毎のお決まりの手続きもしてくれます。

 model.Draw(world, view, projection);

ただし、このヘルパーメソッドはモデル内で使われているエフェクトすべてがIEffectMatricesインターフェースを実装している必要があります。IEffectMatricesを実装していないカスタムエフェクトがあった場合は例外を発生します。この場合は、以前のようにループコードを書いて、それぞれのパラメーターを自前で設定して描画する必要があります。

原文:

http://blogs.msdn.com/b/shawnhar/archive/2010/05/05/effect-interfaces-in-xna-game-studio-4-0.aspx