從零3D基礎入門XNA 4.0(1)——3D開發基礎


【題外話】

最近要做一個3D動畫演示的程序,由於比較熟悉C#語言,再加上XNA對模型的支持比較好,故選擇了XNA平台。不過從網上找到很多XNA的入門文章,發現大都需要一些3D基礎,而我之前並沒有接觸過游戲以及3D相關的開發,所以我來從另一個角度整理下入門XNA。本文盡量少涉及3D及數學方面的知識,因為同類文章介紹的挺多的。

 

【系列索引】

  1. 從零3D基礎入門XNA 4.0(1)——3D開發基礎
  2. 從零3D基礎入門XNA 4.0(2)——模型和BasicEffect

 

【文章索引】

  1. XNA項目的結構
  2. XNA程序的結構
  3. 坐標系與攝像機
  4. 模型的導入與顯示

 

【一、XNA項目的結構】

雖然微軟在推出了XNA 4.0之后就再也沒有升級過XNA的版本,但XNA還是在.NET平台上比較方便的3D框架。由於我使用的是VS2013,而XNA 4.0的安裝程序只認VS2010,所以需要安裝一個使用VS2010 Shell的程序(比如我使用的是SQL Server 2012 Management Studio,當然也可以安裝VS2010 Express)才能通過XNA 4.0的安裝。安裝完后會自動在VS2010下添加相關擴展模板,但不會在更高版本添加,可以參考 http://ryan-lange.com/xna-game-studio-4-0-visual-studio-2012/ 將擴展模板添加到VS2012或2013中(VS2013需要將其中的版本號改為12.0)。

XNA 4.0相對之前的3.1做了很多修改,不僅代碼上進行了很多調整,默認創建的項目也與之前的不同。創建XNA 4.0的Windows Game項目后,默認會創建兩個項目,分別是WindowsGame以及WindowsGameContent,前者存放程序的邏輯代碼,而后者則存放程序所需要的資源(模型、紋理等等),與其他項目不同的是,XNA項目增加了一個Content Reference內容引用,可以將邏輯與資源拆分成不同的項目,即由邏輯代碼的項目引用資源項目,當然也可以合並在一起。

 

【二、XNA程序的結構】

在創建的WindowsGame項目中,與其他Windows程序一樣都包含一個Program.cs,除此之外同時還有一個Game.cs。需要說明的是,與平常開發Windows應用以基於事件的方式不同,開發XNA(以及其他游戲框架)的應用是以基於輪詢的方式。在Game.cs文件中,除了構造方法外還會生成以下幾個方法,其執行順序和微軟給出的說明如下:

  1. protected override void Initialize():需要包含加載任何非圖形的資源。
  2. protected override void LoadContent():在Initialize之后或者在任何需要重新加載資源的時候(比如在DeviceReset事件發生時)執行,需要包含加載游戲需要的圖形資源等。
  3. protected override void Update(GameTime gameTime):在程序邏輯需要處理時執行,需要包含程序狀態的管理、用戶輸入的處理以及數據的更新。
  4. protected override void Draw(GameTime gameTime):在需要繪制一幀畫面的時候執行,需要包含繪制圖形的代碼。
  5. protected override void UnloadContent():在程序需要釋放資源時執行,需要包含釋放資源的代碼。

其中程序的主要部分就是Update()和Draw()兩個方法,整個程序在運行時,幾乎就是這兩個方法在不斷重復執行。需要說明的是,對於XNA,在默認情況下,執行一次Update()和Draw()是要控制在一定時間的(默認為1/60s,即60FPS),如果執行一次Update()和Draw()的時間小於這個時間將會進行等待,如果超過這個時間則會跳幀(不執行Draw()),當然也可以修改Game類中的TargetElapsedTime來改變這個時間,或者修改IsFixedTimeStep=false使得程序幀數能多大就多大。

 

【三、坐標系與攝像機】

對於二維的畫面,我們可以直接使用屏幕的坐標系;而對於三維的畫面,我們還需要將三維世界投影到二維的屏幕上。那么,我們就需要一個計算如何將三維世界投影到二維屏幕的工具,那么攝像機就是實現這個功能的。實際上,這里的攝像機與我們平時拿攝像機錄像是一樣的,在屏幕上顯示的內容就是攝像機錄下的內容。

首先需要說明的是,在XNA中使用的右手坐標系(與Direct3D中使用的左手坐標系Z軸相反),也就是說按正常方向去看的話,向右是X軸正方向,向上是Y軸正方向,而指向自己的(向外)是Z軸正方向,如下圖。

在三維框架中,很多信息的存儲和表示都是用四維矩陣(Matrix類)來的。所以要表示一個攝像機,通常由兩個矩陣組成,分別是 視圖矩陣(View Matrix)投影矩陣(Projection Matrix),其中視圖矩陣表示了攝像機的位置、攝像機的朝向以及攝像機的上方向;而投影矩陣則表示了攝像機的視角以及視覺范圍。雖然聽上去很復雜,但是XNA提供了直接由具體的參數創建矩陣的方法。

對於視圖矩陣的創建,可以使用如下的方法:

Matrix.CreateLookAt(Vector3 cameraPosition, Vector3 cameraTarget, Vector3 cameraUpVector);

其中cameraPosition為攝像機在空間內的三維坐標;cameraTarget為攝像機所指向目標的三維坐標;cameraUpVector則表明了哪個方向是攝像機的向上方向(如果攝像機正着放的話,那么Y軸正方向為攝像機的向上方向)。

而對於投影矩陣的創建,則可以使用如下的方法:

Matrix.CreatePerspectiveFieldOfView(float fieldOfView, float aspectRatio, float nearPlaneDistance, float farPlaneDistance);

其中fieldOfView表示的是攝像機的視角弧度,范圍為(0, Pi),通常為Pi/4(45°);aspectRatio為攝像機的長寬比,通常為屏幕的長寬比;nearPlaneDistance與farPlaneDistance則為當攝像機多近(遠)時無法拍攝到物體。在下圖中表示了fieldOfView與nearPlaneDistance和farPlaneDistance的關系,可以看到攝像機通過視角角度與距離可以產生一個近剪裁面和遠剪裁面,而最終能投影的部分就是處在這兩個平面之間的物體。

 

【四、模型的導入與顯示

對於三維模型,XNA平台支持兩種格式,分別是.x與.fbx文件,其中后者在很多軟件中都支持,比如Maya、MotionBuilder等等。對於模型的導入,只需要將模型文件拖到Content項目中即可。不過需要說明的是,為了保證效率等,XNA程序在運行時並不是調用的fbx等模型文件,而是通過Content Pipeline內容管道進行處理,編譯成擴展名為.xnb的一種中間格式,在程序運行時程序實際調用的為這些中間格式,如下圖

Content Pipeline中主要有兩個重要的部分,分別是Importer以及Content Processor。其中Importer負責將導入的資源文件解析為XNA可以識別的XNA Game Studio Content Document Object Model (DOM)。系統已經支持了很多的文件格式,比如三維模型支持.fbx和.x,紋理支持.bmp、.dds、.dib、.hdr、.jpg、.pfm、.png、.ppm、.tga等文件等,詳情可以參考這里。如果需要的文件格式在XNA框架中不支持,可以自己寫新的Importer來支持更多的格式。

而Processor則根據前者解析后的內容存儲為Output Type,之后再編譯成.xnb文件,在默認情況下使用系統自帶的Processor已經足夠了,不過當想存儲XNA默認沒有存儲的內容時則需要自己擴展Processor。

雖然上述說了這么多,但加載資源則只需要一行代碼即可解決。在上述的Game類中提供了一個ContentManager的實例,名為Content,我們可以使用其來加載我們的模型。ContentManager提供了一個名為Load的泛型方法,將資源類型以及資源的相對路徑傳入即可讀取。比如我們將名為dude.fbx的文件拖到Content項目中,然后只需在上述提到的LoadContent方法中添加如下的一行代碼(需要在Game類中定義一個名為model的Model類型):

protected override void LoadContent()
{
    // TODO: use this.Content to load your game content here

    this.model = this.Content.Load<Model>("dude");
}

而如果要將模型繪制到屏幕上,只要調用Model對象的Draw方法即可。不過Draw方法需要提供 World世界矩陣 以及 View視圖矩陣 和 Projection投影矩陣,對於后兩個矩陣我們上文已經說明,而世界矩陣與視圖矩陣類似,可以使用如下的方法創建:

Matrix.CreateWorld(Vector3 position, Vector3 forward, Vector3 up);

其中position與up均與之前的CreateLookAt類似,為模型在世界中所處的三維坐標和哪個方向是模型的向上方向;而forward則不同,為模型的朝向向量,其僅僅代表方向。當然我們也可以通過創建不同的平移、旋轉等矩陣,然后相乘得到世界矩陣。

接下來我們可以在上述提到的Draw方法中添加如下的代碼:

protected override void Draw(GameTime gameTime)
{
    GraphicsDevice.Clear(Color.CornflowerBlue);

    // TODO: Add your drawing code here

    Matrix world = Matrix.CreateWorld(Vector3.Zero, Vector3.Forward, Vector3.Up);
    Matrix cameraView = Matrix.CreateLookAt(new Vector3(120, 120, 120), Vector3.Zero, Vector3.Up);
    Matrix cameraProjection = Matrix.CreatePerspectiveFieldOfView(MathHelper.PiOver4, this.GraphicsDevice.Viewport.AspectRatio, 10.0F, 10000.0F);
    this.model.Draw(world, cameraView, cameraProjection);

    base.Draw(gameTime);
}

其中Vector3.Zero、Forward、Up為系統預設的幾個常量,分別為(0, 0, 0)、(0, 0, -1)以及(0, 1, 0);而常用弧度值在MathHelper中也可以找到,比如Pi、PiOver2(90度弧度)、PiOver4(45度弧度)等等;GraphicsDevice.Viewport.AspectRatio為顯示區域的寬高比。

這樣我們就可以創建一個在(120, 120, 120)坐標上,朝向坐標原點的攝像機,並在坐標原點創建一個模型(由於dude模型的正面是朝Z軸負方向的,所以這里我們選用的Z軸負方向為模型的朝向)。

文中提到的dude.fbx從微軟提供的sample中獲得:http://xbox.create.msdn.com/en-US/education/catalog/sample/skinned_model

本文所有代碼可以從如下地址下載:http://files.cnblogs.com/mayswind/XNA_Sample_1.zip

雖然現在使用XNA的人越來越少了,但是這個有點類似個人學習筆記的文章還是要正式開坑了。

 

【相關鏈接】

  1. Game Class:http://msdn.microsoft.com/en-us/library/Microsoft.Xna.Framework.Game.aspx
  2. XBOX LIVE Indie Games:http://xbox.create.msdn.com/en-US/education/catalog/?contenttype=4
  3. What is the Content Pipeline?:http://msdn.microsoft.com/en-us/library/bb447745.aspx
  4. Standard Importers and Processors:http://msdn.microsoft.com/zh-cn/library/bb447762
  5. 3D 游戲控制:http://msdn.microsoft.com/zh-cn/windowsphone/gg620050.aspx


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM