การแปลงพิกัดแบบฉายภาพ

ปรับปรุงหน้า :
วันที่สร้างเพจ :

สรุป

การจัดการพารามิเตอร์ของการแปลงพิกัดฉายภาพ

射影座標変換

สภาพแวดล้อมในการทํางาน

ข้อกําหนดเบื้องต้น

รุ่น XNA ที่รองรับ
  • 4.0
แพลตฟอร์มที่รองรับ
  • Windows (XP SP2 หรือใหม่กว่า, Vista, 7)
  • Xbox 360
  • วินโดวส์ โฟน 7
Windows ต้องใช้เวอร์ชัน Vertex Shader 2.0
เวอร์ชัน Pixel Shader ที่จําเป็นของ Windows 2.0

สภาพแวดล้อมในการทํางาน

แท่น
  • หน้าต่าง 7
  • Xbox 360
  • โปรแกรมจําลอง Windows Phone 7

วิธีการทํางานกับตัวอย่าง

เมาส์
แป้นพิมพ์ใช้งานได้คอนโทรลเลอร์ Xbox 360สัมผัส
การเปลี่ยนมุมมอง ↑↓ แท่งซ้าย (ขึ้นและลง) ปุ่มซ้าย (เปลี่ยนอัตโนมัติ, สลับการทํางาน) -
การเปลี่ยนอัตราส่วนภาพ ←→ ไกปืน ปุ่มซ้าย (เปลี่ยนอัตโนมัติ, สลับการทํางาน) -
การเปลี่ยนระนาบคลิปด้านหลัง เอ,ซ แท่งขวา (ขึ้นและลง) ปุ่มซ้าย (เปลี่ยนอัตโนมัติ, สลับการทํางาน) -

สาร

เกี่ยวกับการแปลงพิกัดแบบฉายภาพ

โปรดดูหน้าต่อไปนี้สําหรับภาพรวมของการแปลงพิกัดแบบฉายภาพ

สนาม

มีการเตรียมพารามิเตอร์สี่ตัวเพื่อให้สามารถจัดการพารามิเตอร์ที่ใช้สําหรับการแปลงพิกัดแบบฉายภาพได้: "มุมมอง", "อัตราส่วนภาพ" และ "ตําแหน่งการตัด (ไปข้างหน้า, ถอยหลัง)"

/// <summary>
/// 視野角(radian)
/// </summary>
private float angle = 45.0f;

/// <summary>
/// アスペクト比
/// </summary>
private float aspect = 1.0f;

/// <summary>
/// 手前のクリッピング位置
/// </summary>
private float nearLimit = 1.0f;

/// <summary>
/// 奥のクリッピング位置
/// </summary>
private float farLimit = 100.0f;

การทํางานกับพารามิเตอร์

ในเมธอด Game.Update เราใช้แป้นพิมพ์และคอนโทรลเลอร์เพื่อจัดการแต่ละพารามิเตอร์ สําหรับรายละเอียดของการดําเนินการแต่ละครั้ง โปรดดู "วิธีการ" ที่ด้านบนของหน้านี้ (รหัสด้านล่างคัดลอกมาจากส่วนการทํางานด้วยแป้นเกมเท่านั้น)

ตั้งค่า view มุมกว้างในช่วง "0 < x < π" (ในตัวอย่าง เมื่อเราสร้างการแปลงแบบฉายภาพ เราจะแปลงจากองศาเป็นเรเดียน ดังนั้นที่นี่จึงเป็น "0 < x < 180")

อัตราส่วนภาพไม่ควรน้อยกว่าหรือเท่ากับ 0

ไม่ควรกลับตําแหน่งคลิปไปข้างหน้าและถอยหลัง ต้องไม่ตั้งค่าเป็น 0 หรือน้อยกว่า

GamePadState gamePadState = GamePad.GetState(PlayerIndex.One);

///// 視野角を増減させる /////
float angleSpeed = 0.05f;
this.angle += gamePadState.ThumbSticks.Left.Y * this.angle * angleSpeed;

// 視野角は0~180度までの範囲とする
if (this.angle <= 0.0f)
{
    this.angle = 0.0001f;
}
else if (this.angle >= 180.0f)
{
    this.angle = 179.9999f;
}

/////// アスペクト比 /////
float aspectSpeed = 0.01f;
this.aspect -= gamePadState.Triggers.Left * this.aspect * aspectSpeed;
this.aspect += gamePadState.Triggers.Right * this.aspect * aspectSpeed;

/////// 奥のクリッピング位置 /////
float clipSpeed = 0.05f;
this.farLimit += gamePadState.ThumbSticks.Right.Y * this.farLimit * clipSpeed;

if (this.farLimit <= this.nearLimit)
{
    // 手前のクリッピング位置よりも手前に来てはいけない
    this.farLimit = this.nearLimit + 0.0001f;
}

การสร้างเมทริกซ์การฉายภาพ

เมทริกซ์การฉายภาพถูกสร้างขึ้นโดยการตั้งค่าแต่ละพารามิเตอร์ในเมธอด "Matrix.CreatePerspectiveFieldOfView"

ข้อโต้แย้งแรกคือมุมมองเป็นเรเดียน ในตัวอย่าง หน่วยองศาจะถูกแปลงเป็นเรเดียนโดยใช้วิธี "MathHelper.ToRadians"

อาร์กิวเมนต์ที่สองระบุอัตราส่วนภาพ (อัตราส่วนภาพ) อาร์กิวเมนต์ที่สามระบุตําแหน่งการตัดไปข้างหน้า และอาร์กิวเมนต์ที่สี่ระบุตําแหน่งการตัดด้านหลัง

// プロジェクションマトリックスを作成
Matrix projection = Matrix.CreatePerspectiveFieldOfView(
        MathHelper.ToRadians(this.angle),
        this.aspect,
        this.nearLimit,
        this.farLimit
    );

Matrix.CreatePerspectiveFieldOfView วิธี

สร้างเมทริกซ์การฉายภาพเปอร์สเปคทีฟตามการตั้งค่าของฟิลด์มุมมอง

ฟิลด์ของมุมมอง ลอย มุมมอง. ระบุเป็นหน่วยเรเดียน
อัตราส่วนด้าน ลอย อัตราส่วนภาพ (อัตราส่วนภาพ) โดยปกติคุณจะระบุค่าสําหรับ "ความกว้าง÷ความสูงของมุมมอง"
nearPlaneDistance ลอย ตําแหน่งคลิปไปข้างหน้า วัตถุที่อยู่ด้านหน้าตําแหน่งนี้จะไม่ถูกวาด
ระยะทางเครื่องบินไกล ลอย ตําแหน่งคลิปด้านหลัง วัตถุที่อยู่นอกตําแหน่งนี้จะไม่ถูกวาด
ส่งคืนค่า เมตริกซ์ ส่งกลับเมทริกซ์การฉายภาพเปอร์สเปคทีฟ

ชุดเมทริกซ์การฉายภาพ

มีการตั้งค่าเมทริกซ์การฉายภาพสําหรับแต่ละเอฟเฟกต์

foreach (ModelMesh mesh in this.model.Meshes)
{
    foreach (BasicEffect effect in mesh.Effects)
    {
        // プロジェクションマトリックスを設定
        effect.Projection = projection;
    }

    // モデルを描画
    mesh.Draw();
}

รหัสทั้งหมด

using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
#if WINDOWS_PHONE
using Microsoft.Xna.Framework.Input.Touch;
#endif

namespace ProjectionMatrix
{
    /// <summary>
    /// ゲームメインクラス
    /// </summary>
    public class GameMain : Microsoft.Xna.Framework.Game
    {
        /// <summary>
        /// グラフィックデバイス管理クラス
        /// </summary>
        private GraphicsDeviceManager graphics = null;

        /// <summary>
        /// スプライトのバッチ化クラス
        /// </summary>
        private SpriteBatch spriteBatch = null;

        /// <summary>
        /// スプライトでテキストを描画するためのフォント
        /// </summary>
        private SpriteFont font = null;

        /// <summary>
        /// モデル
        /// </summary>
        private Model model = null;

        /// <summary>
        /// 視野角(radian)
        /// </summary>
        private float angle = 45.0f;

        /// <summary>
        /// アスペクト比
        /// </summary>
        private float aspect = 1.0f;

        /// <summary>
        /// 手前のクリッピング位置
        /// </summary>
        private float nearLimit = 1.0f;

        /// <summary>
        /// 奥のクリッピング位置
        /// </summary>
        private float farLimit = 100.0f;

        /// <summary>
        /// 自動動作フラグ
        /// </summary>
        private int autoMode = 0;

        /// <summary>
        /// マウスボタン押下フラグ
        /// </summary>
        private bool isMousePressed = false;


        /// <summary>
        /// GameMain コンストラクタ
        /// </summary>
        public GameMain()
        {
            // グラフィックデバイス管理クラスの作成
            this.graphics = new GraphicsDeviceManager(this);

            // ゲームコンテンツのルートディレクトリを設定
            this.Content.RootDirectory = "Content";

#if WINDOWS_PHONE
            // Windows Phone のデフォルトのフレームレートは 30 FPS
            this.TargetElapsedTime = TimeSpan.FromTicks(333333);

            // バックバッファサイズの設定
            this.graphics.PreferredBackBufferWidth = 480;
            this.graphics.PreferredBackBufferHeight = 800;

            // フルスクリーン表示
            this.graphics.IsFullScreen = true;
#endif
        }

        /// <summary>
        /// ゲームが始まる前の初期化処理を行うメソッド
        /// グラフィック以外のデータの読み込み、コンポーネントの初期化を行う
        /// </summary>
        protected override void Initialize()
        {
            // TODO: ここに初期化ロジックを書いてください

            // コンポーネントの初期化などを行います
            base.Initialize();
        }

        /// <summary>
        /// ゲームが始まるときに一回だけ呼ばれ
        /// すべてのゲームコンテンツを読み込みます
        /// </summary>
        protected override void LoadContent()
        {
            // テクスチャーを描画するためのスプライトバッチクラスを作成します
            this.spriteBatch = new SpriteBatch(this.GraphicsDevice);

            // フォントをコンテンツパイプラインから読み込む
            this.font = this.Content.Load<SpriteFont>("Font");

            // モデルを作成
            this.model = this.Content.Load<Model>("Model");

            foreach (ModelMesh mesh in this.model.Meshes)
            {
                foreach (BasicEffect effect in mesh.Effects)
                {
                    // デフォルトのライト適用
                    effect.EnableDefaultLighting();

                    // ビューマトリックスをあらかじめ設定
                    effect.View = Matrix.CreateLookAt(
                        new Vector3(3.0f, 3.0f, 6.0f),
                        Vector3.Zero,
                        Vector3.Up
                    );
                }
            }

            // アスペクト比の初期値を設定
            this.aspect = (float)this.GraphicsDevice.Viewport.Width / this.GraphicsDevice.Viewport.Height;
        }

        /// <summary>
        /// ゲームが終了するときに一回だけ呼ばれ
        /// すべてのゲームコンテンツをアンロードします
        /// </summary>
        protected override void UnloadContent()
        {
            // TODO: ContentManager で管理されていないコンテンツを
            //       ここでアンロードしてください
        }

        /// <summary>
        /// 描画以外のデータ更新等の処理を行うメソッド
        /// 主に入力処理、衝突判定などの物理計算、オーディオの再生など
        /// </summary>
        /// <param name="gameTime">このメソッドが呼ばれたときのゲーム時間</param>
        protected override void Update(GameTime gameTime)
        {
            KeyboardState keyState = Keyboard.GetState();
            MouseState mouseState = Mouse.GetState();
            GamePadState gamePadState = GamePad.GetState(PlayerIndex.One);

            // Xbox 360 コントローラ、Windows Phone の BACK ボタンを押したときに
            // ゲームを終了させます
            if (gamePadState.Buttons.Back == ButtonState.Pressed)
            {
                this.Exit();
            }

            // マウスによる自動動作切り替え
            if (this.isMousePressed == false &&
                mouseState.LeftButton == ButtonState.Pressed)
            {
                this.isMousePressed = true;

                this.autoMode = (this.autoMode + 1) % 4;
            }
            this.isMousePressed = mouseState.LeftButton == ButtonState.Pressed;

            ///// 視野角を増減させる /////
            float angleSpeed = 0.05f;
            this.angle += gamePadState.ThumbSticks.Left.Y * this.angle * angleSpeed;
            if (keyState.IsKeyDown(Keys.Down))
            {
                this.angle -= this.angle * angleSpeed;
            }
            if (keyState.IsKeyDown(Keys.Up))
            {
                this.angle += this.angle * angleSpeed;
            }
            if (this.autoMode == 1)
            {
                this.angle = (float)(Math.Sin(gameTime.TotalGameTime.TotalSeconds) + 1.0f) * 90.0f;
            }

            // 視野角は0~180度までの範囲とする
            if (this.angle <= 0.0f)
            {
                this.angle = 0.0001f;
            }
            else if (this.angle >= 180.0f)
            {
                this.angle = 179.9999f;
            }

            /////// アスペクト比 /////
            float aspectSpeed = 0.01f;
            this.aspect -= gamePadState.Triggers.Left * this.aspect * aspectSpeed;
            this.aspect += gamePadState.Triggers.Right * this.aspect * aspectSpeed;
            if (keyState.IsKeyDown(Keys.Left))
            {
                this.aspect -= this.aspect * aspectSpeed;
            }
            if (keyState.IsKeyDown(Keys.Right))
            {
                this.aspect += this.aspect * aspectSpeed;
            }
            if (this.autoMode == 2)
            {
                this.aspect = (float)(Math.Sin(gameTime.TotalGameTime.TotalSeconds) + 1.0f) * 2.0f;
            }

            /////// 奥のクリッピング位置 /////
            float clipSpeed = 0.05f;
            this.farLimit += gamePadState.ThumbSticks.Right.Y * this.farLimit * clipSpeed;
            if (keyState.IsKeyDown(Keys.Z))
            {
                this.farLimit -= this.farLimit * clipSpeed;
            }
            if (keyState.IsKeyDown(Keys.A))
            {
                this.farLimit += this.farLimit * clipSpeed;
            }
            if (this.autoMode == 3)
            {
                this.farLimit = (float)(Math.Sin(gameTime.TotalGameTime.TotalSeconds) + 1.0f) * 8.0f;
            }

            if (this.farLimit <= this.nearLimit)
            {
                // 手前のクリッピング位置よりも手前に来てはいけない
                this.farLimit = this.nearLimit + 0.0001f;
            }

            // 登録された GameComponent を更新する
            base.Update(gameTime);
        }

        /// <summary>
        /// 描画処理を行うメソッド
        /// </summary>
        /// <param name="gameTime">このメソッドが呼ばれたときのゲーム時間</param>
        protected override void Draw(GameTime gameTime)
        {
            // 画面を指定した色でクリアします
            this.GraphicsDevice.Clear(Color.CornflowerBlue);

            // プロジェクションマトリックスを作成
            Matrix projection = Matrix.CreatePerspectiveFieldOfView(
                    MathHelper.ToRadians(this.angle),
                    this.aspect,
                    this.nearLimit,
                    this.farLimit
                );

            foreach (ModelMesh mesh in this.model.Meshes)
            {
                foreach (BasicEffect effect in mesh.Effects)
                {
                    // プロジェクションマトリックスを設定
                    effect.Projection = projection;
                }

                // モデルを描画
                mesh.Draw();
            }

            // スプライトの描画準備
            this.spriteBatch.Begin();

            // テキストをスプライトとして描画する
            this.spriteBatch.DrawString(this.font,
                "Angle : " + this.angle + Environment.NewLine +
                "Aspect : " + this.aspect + Environment.NewLine +
                "NearLimit : " + this.nearLimit + Environment.NewLine +
                "FarLimit : " + this.farLimit + Environment.NewLine +
                "MousePressAutoMode : " + this.autoMode,
                new Vector2(50, 50), Color.White);

            // スプライトの一括描画
            this.spriteBatch.End();

            // 登録された DrawableGameComponent を描画する
            base.Draw(gameTime);
        }
    }
}