พิกัดสามมิติเป็นการแปลงพิกัดหน้าจอ
สรุป
โดยจะแปลงพิกัดในพื้นที่ 3 มิติเป็นพิกัดบนหน้าจอและแสดงชื่อที่ตําแหน่งของโมเดล ในตัวอย่าง กล้องจะถูกหมุนเพื่อให้เห็นได้ง่ายขึ้นว่าชื่อตามโมเดล
สภาพแวดล้อมในการทํางาน
ข้อกําหนดเบื้องต้น
รุ่น XNA ที่รองรับ |
|
แพลตฟอร์มที่รองรับ |
|
Windows ต้องใช้เวอร์ชัน Vertex Shader | 2.0 |
เวอร์ชัน Pixel Shader ที่จําเป็นของ Windows | 2.0 |
สภาพแวดล้อมในการทํางาน
แท่น |
|
สาร
การแปลงพิกัด 3 มิติเป็นพิกัดหน้าจอ
หากคุณต้องการแสดงชื่อเหนือศีรษะของตัวละคร เช่น ในเกม 3 มิติ จําเป็นต้องคํานวณพิกัดหน้าจอที่ควรแสดงตัวละครจากตําแหน่งเชิงพื้นที่สามมิติของตัวละคร
อาจดูเหมือนเป็นการคํานวณที่ยาก แต่ในความเป็นจริงข้อมูลจุดยอดของรูปหลายเหลี่ยมนั้นได้มาในลักษณะเดียวกับสูตรที่ใช้ในการแปลงข้อมูลจุดยอดของรูปหลายเหลี่ยมจากพิกัด 3 มิติเป็นพิกัดหน้าจอดังนั้นจึงไม่จําเป็นต้องมีความรู้ใหม่
นอกจากนี้ยังมีวิธีการที่ง่ายต่อการคํานวณ คุณจึงไม่ต้องเขียนโค้ดเพิ่มเติมใดๆ
สนาม
ในตัวอย่าง สามโมเดลจะปรากฏขึ้น ดังนั้นเราจึงกําหนดโมเดลและเวกเตอร์[3] ซึ่งเป็นข้อมูลตําแหน่งสามตําแหน่ง นอกจากนี้ยังกําหนดมุมการหมุนอัตโนมัติสําหรับกล้องเพื่อให้เข้าใจการเคลื่อนไหวได้ง่าย
<summary>
モデル
</summary>
private Model model = null;
<summary>
位置の配列
</summary>
private Vector3[] positions = new Vector3[3];
<summary>
カメラの水平回転角度
</summary>
private float theta = 0.0f;
กระบวนการอัปเดต
มุมการหมุนสําหรับการหมุนอัตโนมัติของกล้องได้มาจากเวลาที่ผ่านไปของเกม
// カメラの水平角度を自動更新
this.theta = (float)gameTime.TotalGameTime.TotalSeconds / 2.0f;
การวาดภาพล่วงหน้า
หลังจากวาดสไปรท์แล้ว บัฟเฟอร์ความลึกอาจถูกปิดใช้งาน ดังนั้นตั้งค่า "DepthStencilState.Default" เพื่อเปิดใช้งานการตัดสินการวาดภาพตามค่าความลึก
ด้วยการคูณเมทริกซ์การหมุนแกน Y ด้วยเมทริกซ์มุมมองที่คุณสร้างไว้แล้ว คุณจะสามารถหมุนตําแหน่งของกล้องรอบจุดสนใจได้ ให้ความสนใจกับลําดับการคูณเมทริกซ์
// Zバッファを有効にする
this.GraphicsDevice.DepthStencilState = DepthStencilState.Default;
// ビューマトリックスに回転を合成算出
Matrix rotatedView = Matrix.CreateRotationY(this.theta) * this.view;
รับวิวพอร์ต
วิวพอร์ตมีวิธีการแปลงจากพิกัดเชิงพื้นที่ 3 มิติเป็นพิกัดหน้าจอ ดังนั้นรับจาก GraphicsDevice
// ビューポート取得
Viewport viewport = this.GraphicsDevice.Viewport;
การคํานวณพิกัดหน้าจอจากพิกัด 3 มิติ
การค้นหาพิกัดหน้าจอจากพิกัดพื้นที่ 3 มิติทําได้ง่ายเพียงแค่ใช้เมธอด Viewport.Project
ระบุตําแหน่งเชิงพื้นที่ 3 มิติของแหล่งที่มาเป็นอาร์กิวเมนต์แรกเมทริกซ์การฉายภาพเป็นอาร์กิวเมนต์ที่สองและเมทริกซ์มุมมองเป็นอาร์กิวเมนต์ที่สาม จากนั้นคุณสามารถรับพิกัดบนหน้าจอเป็นค่าที่ส่งคืน
หลังจากนั้นหากคุณแยกค่า X และ Y ออกจาก Vector3 คุณสามารถใช้ตําแหน่งที่แสดงของข้อความได้ (Z คือตําแหน่งความลึก)
// 3次元座標からスクリーンの座標算出
Vector3 v3 = viewport.Project(this.positions[i],
this.projection,
rotatedView,
Matrix.Identity);
Vector2 screenPosition = new Vector2(v3.X, v3.Y);
// テキスト描画
this.spriteBatch.DrawString(this.font, "Model " + (i + 1).ToString(),
screenPosition, Color.White);
Viewport.Project
วิธี
ฉายเวกเตอร์ 3 มิติจากพื้นที่วัตถุไปยังพื้นที่หน้าจอ
ที่มา | เวกเตอร์ 3 | เวกเตอร์พิกัดเชิงพื้นที่ 3 มิติสําหรับการฉายภาพลงในพิกัดหน้าจอ |
ฉาย ภาพ | เมตริกซ์ | เมทริกซ์ฉายภาพ |
ทิวทัศน์ | เมตริกซ์ | ดูเมทริกซ์ |
โลก | เมตริกซ์ | ระบุการแปลงแรกที่จะดําเนินการ |
ส่งคืนค่า | เวกเตอร์ 3 | รับเวกเตอร์บนพื้นที่หน้าจอ |
รหัสทั้งหมด
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 TransformToScreen
{
<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>
位置の配列
</summary>
private Vector3[] positions = new Vector3[3];
<summary>
カメラの水平回転角度
</summary>
private float theta = 0.0f;
<summary>
ビューマトリックス
</summary>
private Matrix view = Matrix.Identity;
<summary>
プロジェクションマトリックス
</summary>
private Matrix projection = Matrix.Identity;
<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()
{
// 位置をランダムに設定
Random rand = new Random();
for (int i = 0; i < this.positions.Length; i++)
{
this.positions[i] =
new Vector3((float)(rand.NextDouble() - 0.5) * 10.0f,
0.0f,
(float)(rand.NextDouble() - 0.5) * 10.0f);
}
// ビューマトリックス
this.view = Matrix.CreateLookAt(new Vector3(0.0f, 10.0f, 20.0f),
Vector3.Zero,
Vector3.Up);
// プロジェクションマトリックス
this.projection = Matrix.CreatePerspectiveFieldOfView(
MathHelper.ToRadians(45.0f),
(float)this.GraphicsDevice.Viewport.Width /
(float)this.GraphicsDevice.Viewport.Height,
1.0f,
100.0f
);
// コンポーネントの初期化などを行います
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.Projection = this.projection;
}
}
}
<summary>
ゲームが終了するときに一回だけ呼ばれ
すべてのゲームコンテンツをアンロードします
</summary>
protected override void UnloadContent()
{
// TODO: ContentManager で管理されていないコンテンツを
// ここでアンロードしてください
}
<summary>
描画以外のデータ更新等の処理を行うメソッド
主に入力処理、衝突判定などの物理計算、オーディオの再生など
</summary>
<param name="gameTime">このメソッドが呼ばれたときのゲーム時間</param>
protected override void Update(GameTime gameTime)
{
// Xbox 360 コントローラ、Windows Phone の BACK ボタンを押したときに
// ゲームを終了させます
if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
{
this.Exit();
}
// カメラの水平角度を自動更新
this.theta = (float)gameTime.TotalGameTime.TotalSeconds / 2.0f;
// 登録された GameComponent を更新する
base.Update(gameTime);
}
<summary>
描画処理を行うメソッド
</summary>
<param name="gameTime">このメソッドが呼ばれたときのゲーム時間</param>
protected override void Draw(GameTime gameTime)
{
// 画面を指定した色でクリアします
this.GraphicsDevice.Clear(Color.CornflowerBlue);
// Zバッファを有効にする
this.GraphicsDevice.DepthStencilState = DepthStencilState.Default;
// ビューマトリックスに回転を合成算出
Matrix rotatedView = Matrix.CreateRotationY(this.theta) * this.view;
for (int i = 0; i < this.positions.Length; i++)
{
foreach (ModelMesh mesh in this.model.Meshes)
{
foreach (BasicEffect effect in mesh.Effects)
{
// ビューマトリックス
effect.View = rotatedView;
// モデルの位置設定
effect.World = Matrix.CreateTranslation(this.positions[i]);
}
// モデルを描画
mesh.Draw();
}
}
// ビューポート取得
Viewport viewport = this.GraphicsDevice.Viewport;
// スプライトの描画準備
this.spriteBatch.Begin();
// 各モデルの位置にテキストを描画
for (int i = 0; i < this.positions.Length; i++)
{
// 3次元座標からスクリーンの座標算出
Vector3 v3 = viewport.Project(this.positions[i],
this.projection,
rotatedView,
Matrix.Identity);
Vector2 screenPosition = new Vector2(v3.X, v3.Y);
// テキスト描画
this.spriteBatch.DrawString(this.font, "Model " + (i + 1).ToString(),
screenPosition, Color.White);
}
// スプライトの一括描画
this.spriteBatch.End();
// 登録された DrawableGameComponent を描画する
base.Draw(gameTime);
}
}
}