天天看點

從零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檔案中,除了構造方法外還會生成以下幾個方法,其執行順序和微軟給出的說明如下:

從零3D基礎入門XNA 4.0(1)——3D開發基礎
  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軸正方向,如下圖。

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

在三維架構中,很多資訊的存儲和表示都是用四維矩陣(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的關系,可以看到錄影機通過視角角度與距離可以産生一個近剪裁面和遠剪裁面,而最終能投影的部分就是處在這兩個平面之間的物體。

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

【四、模型的導入與顯示】

對于三維模型,XNA平台支援兩種格式,分别是.x與.fbx檔案,其中後者在很多軟體中都支援,比如Maya、MotionBuilder等等。對于模型的導入,隻需要将模型檔案拖到Content項目中即可。不過需要說明的是,為了保證效率等,XNA程式在運作時并不是調用的fbx等模型檔案,而是通過Content Pipeline内容管道進行處理,編譯成擴充名為.xnb的一種中間格式,在程式運作時程式實際調用的為這些中間格式,如下圖。

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

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軸負方向為模型的朝向)。

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

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

本文所有代碼可以從如下位址下載下傳:https://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

如果您覺得本文對您有所幫助,不妨點選下方的“推薦”按鈕來支援我!

本文及文章中代碼均基于“署名-非商業性使用-相同方式共享 3.0”,文章歡迎轉載,但請您務必注明文章的作者和出處連結,如有疑問請私信我聯系!

繼續閱讀