【題外話】html
最近要作一個3D動畫演示的程序,因爲比較熟悉C#語言,再加上XNA對模型的支持比較好,故選擇了XNA平臺。不過從網上找到不少XNA的入門文章,發現大都須要一些3D基礎,而我以前並無接觸過遊戲以及3D相關的開發,因此我來從另外一個角度整理下入門XNA。本文儘可能少涉及3D及數學方面的知識,由於同類文章介紹的挺多的。windows
【系列索引】框架
【文章索引】ide
雖然微軟在推出了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)。visual-studio
XNA 4.0相對以前的3.1作了不少修改,不只代碼上進行了不少調整,默認建立的項目也與以前的不一樣。建立XNA 4.0的Windows Game項目後,默認會建立兩個項目,分別是WindowsGame以及WindowsGameContent,前者存放程序的邏輯代碼,然後者則存放程序所須要的資源(模型、紋理等等),與其餘項目不一樣的是,XNA項目增長了一個Content Reference內容引用,能夠將邏輯與資源拆分紅不一樣的項目,即由邏輯代碼的項目引用資源項目,固然也能夠合併在一塊兒。學習
在建立的WindowsGame項目中,與其餘Windows程序同樣都包含一個Program.cs,除此以外同時還有一個Game.cs。須要說明的是,與日常開發Windows應用以基於事件的方式不一樣,開發XNA(以及其餘遊戲框架)的應用是以基於輪詢的方式。在Game.cs文件中,除了構造方法外還會生成如下幾個方法,其執行順序和微軟給出的說明以下:ui
其中程序的主要部分就是Update()和Draw()兩個方法,整個程序在運行時,幾乎就是這兩個方法在不斷重複執行。須要說明的是,對於XNA,在默認狀況下,執行一次Update()和Draw()是要控制在必定時間的(默認爲1/60s,即60FPS),若是執行一次Update()和Draw()的時間小於這個時間將會進行等待,若是超過這個時間則會跳幀(不執行Draw()),固然也能夠修改Game類中的TargetElapsedTime來改變這個時間,或者修改IsFixedTimeStep=false使得程序幀數能多大就多大。this
對於二維的畫面,咱們能夠直接使用屏幕的座標系;而對於三維的畫面,咱們還須要將三維世界投影到二維的屏幕上。那麼,咱們就須要一個計算如何將三維世界投影到二維屏幕的工具,那麼攝像機就是實現這個功能的。實際上,這裏的攝像機與咱們平時拿攝像機錄像是同樣的,在屏幕上顯示的內容就是攝像機錄下的內容。
首先須要說明的是,在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的人愈來愈少了,可是這個有點相似我的學習筆記的文章仍是要正式開坑了。
【相關連接】