カウンタ

  モデルの拡大縮小

Google
▲探し物はこちら

 今回はモデルを各XZ方向に拡大してみたいと思います。これを使えば爆発などのエフェクトシーンなどに柔軟に対応することができます。
 キーボードの「↑↓←→」で XZ 方向にそれぞれモデルが拡大縮小します。

モデルの拡大縮小

 下のリンクから今回のプロジェクトをダウンロードできます。

ファイル名 言語 サイズ バージョン
scaling_cs_1_1.zip C# 56KB 1.1
scaling_vb_1_1.zip VB.NET 62KB 1.1
scaling_cpp_1_1.zip C++/CLI 29KB 1.1

 今回のメインコードファイルを載せます。重要なコードを赤色で表示させています

MainSample.cs

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using Microsoft.DirectX;
using Microsoft.DirectX.Direct3D;

namespace MDXSample
{
    /// <summary>
    /// メインサンプルクラス
    /// </summary>
    public partial class MainSample : IDisposable
    {
        /// <summary>
        /// モデルの拡大縮小情報
        /// </summary>
        private Vector3 _scale = new Vector3(1.0f, 1.0f, 1.0f);


        /// <summary>
        /// アプリケーションの初期化
        /// </summary>
        /// <param name="topLevelForm">トップレベルウインドウ</param>
        /// <returns>全ての初期化がOKなら true, ひとつでも失敗したら false を返すようにする</returns>
        /// <remarks>
        /// false を返した場合は、自動的にアプリケーションが終了するようになっている
        /// </remarks>
        public bool InitializeApplication(MainForm topLevelForm)
        {
            // フォームの参照を保持
            this._form = topLevelForm;

            // 入力イベント作成
            this.CreateInputEvent(topLevelForm);

            try
            {
                // Direct3D デバイス作成
                this.CreateDevice(topLevelForm);

                // フォントの作成
                this.CreateFont();

                // Xファイルからメッシュ作成
                this.LoadXFileMesh("Deruderu.x");
            }
            catch (DirectXException ex)
            {
                // 例外発生
                MessageBox.Show(ex.ToString(), "エラー", MessageBoxButtons.OK, MessageBoxIcon.Error);
                return false;
            }

            // XYZライン作成
            this.CreateXYZLine();

            // ライトを設定
            this.SettingLight();

            // カメラ空間にトランスフォームされた後で頂点法線の正規化
            this._device.RenderState.NormalizeNormals = true;

            return true;
        }

        /// <summary>
        /// メインループ処理
        /// </summary>
        public void MainLoop()
        {
            // カメラの設定
            this.SettingCamera();


            // キー操作で拡大縮小パラメータを変化
            if (this._keys[(int)Keys.Left])
            {
                this._scale.X /= 1.01f;
            }
            if (this._keys[(int)Keys.Right])
            {
                this._scale.X *= 1.01f;
            }
            if (this._keys[(int)Keys.Down])
            {
                this._scale.Z /= 1.01f;
            }
            if (this._keys[(int)Keys.Up])
            {
                this._scale.Z *= 1.01f;
            }


            // 描画内容を単色でクリアし、Zバッファもクリア
            this._device.Clear(ClearFlags.Target | ClearFlags.ZBuffer, Color.DarkBlue, 1.0f, 0);

            // 「BeginScene」と「EndScene」の間に描画内容を記述する
            this._device.BeginScene();


            // ライトを無効
            this._device.RenderState.Lighting = false;

            // 原点に配置
            this._device.SetTransform(TransformType.World, Matrix.Identity);

            // XYZラインを描画
            this.RenderXYZLine();


            // ライトを有効
            this._device.RenderState.Lighting = true;

            // 拡大縮小のための座標変換
            this._device.SetTransform(TransformType.World, Matrix.Scaling(this._scale));

            // メッシュの描画
            this.RenderMesh();


            // 文字列の描画
            this._font.DrawText(null, "[←→]±X [↑↓]±Z", 0, 0, Color.White);
            this._font.DrawText(null, "θ:" + this._lensPosTheta, 0, 12, Color.White);
            this._font.DrawText(null, "φ:" + this._lensPosPhi, 0, 24, Color.White);
            this._font.DrawText(null, "X :" + this._scale.X, 0, 36, Color.White);
            this._font.DrawText(null, "Y :" + this._scale.Y, 0, 48, Color.White);
            this._font.DrawText(null, "Z :" + this._scale.Z, 0, 60, Color.White);

            // 描画はここまで
            this._device.EndScene();

            // 実際のディスプレイに描画
            this._device.Present();
        }

        /// <summary>
        /// リソースの破棄をするために呼ばれる
        /// </summary>
        public void Dispose()
        {
            // メッシュの破棄
            this.DisposeMesh();

            // XYZラインの破棄
            this.DisposeXYZLine();

            // フォントのリソースを解放
            if (this._font != null)
            {
                this._font.Dispose();
            }

            // Direct3D デバイスのリソース解放
            if (this._device != null)
            {
                this._device.Dispose();
            }
        }
    }
}

 では、赤文字の部分を説明していきます。MainSamplePartial.cs ファイルのコードはこちらです。


/// <summary>
/// モデルの拡大縮小情報
/// </summary>
private Vector3 _scale = new Vector3(1.0f, 1.0f, 1.0f);

 モデルを各XYZ方向に拡大するため「Vector3」のパラメータを用意します。拡大縮小の値は原型の「倍率」なので各値を「1.0」で初期化します。あやまって「Vector3.Empty」なんかを指定してしまうとモデルが表示されなくなってしまうので注意してください。


// カメラ空間にトランスフォームされた後で頂点法線の正規化
this._device.RenderState.NormalizeNormals = true;

 モデルを拡大すると法線の計算が変わってしまうので、座標変換した後に法線を正規化するように指定します。


// キー操作で拡大縮小パラメータを変化
if (this._keys[(int)Keys.Left])
{
    this._scale.X /= 1.01f;
}
if (this._keys[(int)Keys.Right])
{
    this._scale.X *= 1.01f;
}
if (this._keys[(int)Keys.Down])
{
    this._scale.Z /= 1.01f;
}
if (this._keys[(int)Keys.Up])
{
    this._scale.Z *= 1.01f;
}

 モデルの拡大縮小が自然に見えるように、元の値に「1.01」をかけたり割ったりしています。もちろん値は適当でかまいません(拡大縮小のスピードにもなります)。
 さらにこうすることによって、モデルの拡大率がマイナスになったりすることを極力防ぐことができます。


// ライトを有効
this._device.RenderState.Lighting = true;

// 拡大縮小のための座標変換
this._device.SetTransform(TransformType.World, Matrix.Scaling(this._scale));

// メッシュの描画
this.RenderMesh();

 今回拡大縮小に使用するメソッドはずばりそのまま「Matrix.Scaling」です。値をセットした「Vector3」を渡せばそのとおりに拡大縮小してくれます。

 他は前と一緒なので説明はこれだけです。計算方法など詳しい説明を見たい方はこの続きを読んでください。


 「Matrix.Scaling」メソッドは、渡された値を下のようなマトリックスに変換しています。

拡大マトリックス

 ではポリゴンを使って説明します。下の図はY軸方向(真上)から見た図だと思ってください。

初期位置

 今回はポリゴンの倍率を(1.5, 1.0, 2.0)とします。
 まず最初に0番の頂点を計算して見ます。頂点の位置ベクトルは4次元ベクトルに変換します。

座標変換

 残りの頂点は下のような結果になります。

  • 0番目 : (-1.5, 0, 2)
  • 1番目 : (3, 0, 2)
  • 2番目 : (-1.5, 0, -2)
  • 3番目 : (3, 0, -2)

 下が図で表した結果です。

拡大結果

その他の関連情報です▼