ビルボードのビルさん

■今回はビルボードに関して書こうと思います。
ビルボード用にモデルデータ(板ポリゴンのみ)を用意しました。
「borad.x」

xof 0302txt 0064

Mesh {
 4;
 -1.00000;1.00000;0.00000;,
 1.00000;1.00000;0.00000;,
 1.00000;-1.00000;0.00000;,
 -1.00000;-1.00000;0.00000;;
 
 1;
 4;0,1,2,3;;
 
 MeshTextureCoords {
 4;
 0.00000;0.00000;,
 1.00000;0.00000;,
 1.00000;1.00000;,
 0.00000;1.00000;;
 }
 MeshMaterialList {
  1;
  1;
  0;
  Material {
   1.000000;1.000000;1.000000;1.000000;;
   0.000000;
   0.000000;0.000000;0.000000;;
   0.000000;0.000000;0.000000;;

   TextureFilename {
    "bill.jpg";
   }
  }
 }
}

今回はせっかくビルボードなので、テクスチャにビルさんが登場です。
■最初のソースはこんな感じて

namespace WindowsGame2_08
{
    public class Game1 : Microsoft.Xna.Framework.Game
    {
        GraphicsDeviceManager graphics;
        private double dAng = 0;
        private Model model;
        private Matrix mainView;
        private Matrix mainWorld;
        private Matrix mainProjection;

        public Game1()
        {
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";
        }
        protected override void Initialize()
        {
            model = Content.Load<Model>("borad");
            mainProjection = Matrix.CreatePerspectiveFieldOfView(
                    MathHelper.ToRadians(45.0f),
                    (float)graphics.GraphicsDevice.Viewport.Width / (float)graphics.GraphicsDevice.Viewport.Height,
                    1.0f,
                    100.0f
                );
            mainWorld = Matrix.CreateTranslation(0, 0, 0);
            base.Initialize();
        }

        protected override void Update(GameTime gameTime)
        {
            // ビューマトリックス
            mainView = Matrix.CreateLookAt(
                    new Vector3((float)(10.0 * Math.Sin(dAng)), 5.0f, (float)(10.0 * Math.Cos(dAng))),
                    new Vector3(0, 0, 0),
                    Vector3.Up
                );
            dAng += 0.01;
            if (dAng > 3.141592 * 2)
                dAng -= 3.141592 * 2;
            //-----------------------
            base.Update(gameTime);
        }

        protected override void Draw(GameTime gameTime)
        {
            graphics.GraphicsDevice.Clear(Color.CornflowerBlue);
            for (int j = -4; j < 5; j++)
            {
                for (int i = -4; i < 5; i++)
                {
                    mainWorld = Matrix.CreateTranslation(i * 3.0f, 0.0f, j * 3.0f);
                    foreach (ModelMesh mesh in model.Meshes)
                    {
                        foreach (BasicEffect effect in mesh.Effects)
                        {
                            effect.View = mainView;
                            effect.World = mainWorld;
                            effect.Projection = mainProjection;
                        }
                        mesh.Draw();
                    }
                }
            }
            base.Draw(gameTime);
        }
    }
}

このプログラムを実行すると、ポリゴンは回転に合わせて、向きを変え、こっちをむいてくれません。

■こっちを向いてもらうために少し工夫が必要で、描画部分を修正します。

protected override void Draw(GameTime gameTime)
{
    Matrix viewInvert = Matrix.Invert(mainView);
    viewInvert.M14 = 0.0f;
    viewInvert.M24 = 0.0f;
    viewInvert.M34 = 0.0f;
    viewInvert.M41 = 0.0f;
    viewInvert.M42 = 0.0f;
    viewInvert.M43 = 0.0f;
    viewInvert.M44 = 1.0f;

    graphics.GraphicsDevice.Clear(Color.CornflowerBlue);
    for (int j = -4; j < 5; j++)
    {
        for (int i = -4; i < 5; i++)
        {
            mainWorld = viewInvert * Matrix.CreateTranslation(i * 3.0f, 0.0f, j * 3.0f);
            foreach (ModelMesh mesh in model.Meshes)
            {
                foreach (BasicEffect effect in mesh.Effects)
                {
                    effect.View = mainView;
                    effect.World = mainWorld;
                    effect.Projection = mainProjection;
                }
                mesh.Draw();
            }
        }
    }
    base.Draw(gameTime);
}

な感じで、Viewマトリックから逆ベクトルを求めて、モデルを必ず正面に向ける準備をします。

    Matrix viewInvert = Matrix.Invert(mainView);
    viewInvert.M14 = 0.0f;
    viewInvert.M24 = 0.0f;
    viewInvert.M34 = 0.0f;
    viewInvert.M41 = 0.0f;
    viewInvert.M42 = 0.0f;
    viewInvert.M43 = 0.0f;
    viewInvert.M44 = 1.0f;

ここで生成したマトリックスをワールドマトリックスに掛け合わせておきます。

mainWorld = viewInvert * Matrix.CreateTranslation(i * 3.0f, 0.0f, j * 3.0f);

■そうすると、いつでもビルさんがこっちをむいてくれるようになりました。