跳轉至內容

使用XNA/3D開發/攝像機和燈光建立遊戲

來自Wikibooks,開放世界中的開放書籍



攝像機

[編輯 | 編輯原始碼]

攝像機是3D世界中非常重要的組成部分,因為它代表了使用者的視角。在開始時,必須定義攝像機的兩個基本要素:位置和觀察方向,然後XNA才能將內容渲染到您的3D世界中。

座標系

[編輯 | 編輯原始碼]

您需要注意的是,不同的圖形系統使用不同的軸系。XNA使用右手座標系。X表示右,Y表示上,Z表示螢幕外。將一個系統轉換為另一個系統的操作是透過反轉任意一個軸(但只能反轉一個)來完成的。

角度和弧度

[編輯 | 編輯原始碼]
角度 PI
45度 1/4 PI
90度 1/2 PI
180度 PI
270度 3/2 PI
360度 2 PI

數學輔助函式MathHelper.ToDegree(radians)MathHelper.ToRadians(degrees)可以幫助您進行轉換。

矩陣和空間

[編輯 | 編輯原始碼]

在渲染任何3D幾何圖形之前,必須設定3個矩陣。

  • 世界矩陣
    將物件/模型空間轉換為世界空間的2D變換
您從Maya、3ds Max等軟體中匯入的模型包含一堆頂點位置,這些位置與該物件的中心有關。要使用這些資料,您需要使用世界矩陣將其從所謂的物件/模型空間轉換為世界空間中的物件。
Matrix worldTranslation=Matrix.CreateTranslation(new Vector3(x,y,z));
使用此函式,您可以透過使用向量建立一個將物件的位置轉換為世界空間的矩陣。轉換後,您可以縮放、旋轉和平移物件。但請記住,矩陣乘法不滿足交換律,在XNA中,您始終需要按照S-R-T的順序執行此操作。
  • 視矩陣
    將世界空間轉換為視空間的2D變換
要從特定點觀察您的世界,必須使用視矩陣將其從世界空間轉換為視空間。
  • 投影矩陣
實際看到的已檢視3D資料(稱為視錐體)必須轉換為您的2D螢幕。必須使用投影矩陣將視空間轉換為螢幕空間。



攝像機設定

[編輯 | 編輯原始碼]

如果您想在2D螢幕上為使用者視覺化您的3D內容,則需要使攝像機工作。您可以透過使用上面提到的視矩陣和投影矩陣來完成此操作,它們會根據您的需要轉換資料。

視矩陣

[編輯 | 編輯原始碼]

它儲存攝像機的位置和觀察方向——為此,您必須設定攝像機的Position、Target和Up向量。您可以使用Matrix.CreateLookAt方法來完成此操作。

viewMatrix = Matrix.CreateLookAt(camPosition, camTarget, camUpVector);

這三個引數是向量。

  • 位置向量很容易解釋:它顯示了攝像機在3D世界中的位置。
  • 目標向量也很容易解釋:它顯示了攝像機在3D世界中觀察的點。
  • 向上向量很重要。想象一下,您手中拿著一個手機,它是您的攝像機。它自動為您提供了位置向量。下一步是聚焦您想要拍攝的目標。現在,您獲得了位置向量和目標向量的具體值,但仍然有許多方法可以透過圍繞其中心旋轉來握住手機。位置向量和目標向量保持不變,但由於旋轉,您可以拍攝的照片會發生變化。這就是為什麼您需要宣告哪個方向是向上的原因。只有在設定了這三個向量後,您才能獲得一個唯一的攝像機。


這部分的完整程式碼可能如下所示

Matrix viewMatrix;
Vector3 camPosition = new Vector3(x,y,z);
Vector3 camTarget = new Vector3(x,y,z);
Vector3 camUpVector = new Vector3(x,y,z);

viewMatrix = Matrix.CreateLookAt(camPosition, camTarget, camUpVector);


投影矩陣

[編輯 | 編輯原始碼]

它儲存視錐體,即透過攝像機看到的3D世界中的所有內容,並應渲染到您的2D螢幕上。將您的攝像機視為一個點。現在建立兩個矩形/層,一個靠近的較小的矩形和一個較大的遠的矩形。繪製一條從攝像機點開始並連線兩個矩形/層的每個右上角的線,然後對兩個矩形/層的其他三個角執行相同的操作。如果您這樣做,您將得到一個金字塔,其錐形端是攝像機點,底部是較大的矩形/層。這兩者之間的一切都稱為視景體。近平面和遠平面之間的空間稱為視錐體視錐體中的所有細節都將渲染到您的2D螢幕上。

建立投影矩陣的方法稱為Matrix.CreatePerspectiveFieldOfView,應如下所示

projectionMatrix=Matrix.CreatePerspectiveFieldOfView(2f * (float)Math.Atan((float)Math.Tan(fieldOfView / 2f) / (aspectAxisConstraint == (int)aspectAxis.Horizontal ? zoomFactor : aspectRatio / originalAspect / zoomFactor)), aspectRatio, nearPlaneDistance, farPlaneDistance);
  • fieldOfView指定y方向的視野(弧度)。
  • aspectRatio是視空間寬度除以視空間高度的比值。包含渲染的3D世界的2D螢幕的縱橫比。
  • nearPlaneDistance是攝像機與近平面之間的距離。
  • farPlaneDistance是攝像機與遠平面之間的距離。

其他與檢視相關的引數(例如更改縱橫比軸的約束(以保持水平或垂直視空間,或使用指定的縱橫比在低於該縱橫比時更改方向)和縮放因子)可以在其結構體中指定為子引數,並且可以像這樣更改

public enum AspectAxis : int
{
    Horizontal,
    Vertical
}
float originalAspect = 16f / 9f
float zoomFactor = 1f;
int aspectAxisConstraint = aspectAxis.Vertical;

上面兩個FOV縮放子引數的預設值均為1。

例如,如果約束設定為1,原始縱橫比設定為1.777777777778,當前縱橫比為1.3333333333,則4:3解析度下的檢視將比16:9更高。

近平面和遠平面也稱為裁剪平面。請記住,前面的大型物件可能會擋住後面幾乎大部分的3D世界,因此使用此平面將它們裁剪掉。對於非常小的遠處的物件也適用,也許它們幾乎不可見,但需要渲染它們。如果要節省資源,請裁剪它們。

  • 世界矩陣計算您想要渲染的每個資料及其位置。
  • 檢視矩陣將在使用者輸入導致位置或方向發生變化時每次都會被重新計算。
  • 投影矩陣僅在視窗縱橫比發生變化時才會被重新計算。因此,這通常發生在遊戲的開始。



照亮場景似乎非常簡單。將您的3D物件附加到您的世界中,使用上面提到的矩陣集,透過定義燈光的位置將燈光引入,一切就完成了。但它並不那麼容易,如果沒有正確的燈光設定,您的3D場景看起來不會很逼真。

每個3D物件都由三角形組成,這些三角形必須正確地被照亮。為此,您需要為每個三角形指定一個法向量。請記住準確設定它;法向量應該指向物件外部,如果它指向內部,則不會被正確照亮。藉助光線方向和法線方向的資訊,顯示卡可以計算需要“繪製”到三角形表面的光量。如果光線方向和法線方向垂直,則沒有光線照射,投影為0。如果兩個向量平行,則投影最大;表面將以最大強度被照亮。

現在您需要一個VertexPositionColorTexture類的例項,它應該如下所示

dataVertices[0] =  new VertexPositionNormalTexture(new Vector3(x,y,z), new Vector3(x,y,z), new Vector2(x,y));
  • 一個用於xyz位置的Vector3
  • 一個用於xyz表面法線的Vector3
  • 一個用於uv紋理座標的Vector2


BasicEffect

[編輯 | 編輯原始碼]

如果您想使用基本的燈光效果,您可以使用XNA中的BasicEffect類。使用它,您可以快速設定帶燈光的3D世界。此程式碼可能如下所示

BasicEffect basicEffect;
basicEffect = new BasicEffect(GraphicsDevice, null);

設定變數並例項化它

basicEffect.World = worldMatrix;
basicEffect.View = viewMatrix;
basicEffect.Projection = projectionMatrix;
basicEffect.TextureEnabled = true;

設定上面提到的世界、檢視和投影矩陣。如果您使用紋理,則需要啟用它們。

basicEffect.LightingEnabled = true;
basicEffect.AmbientLightColor = new Vector3(0.1f, 0.1f, 0.1f); ;

啟用燈光設定並定義一個環境顏色,以便您的物件始終被燈光照亮。

basicEffect.DirectionalLight0.Direction = new Vector3(x,y,z);
basicEffect.DirectionalLight0.DiffuseColor = new Vector3(0, 0, 0.5f);
basicEffect.DirectionalLight0.Enabled = true; 

您可以定義不同的光源(最多三個),設定方向和顏色並啟用它們


最後...

effect.Begin();
foreach (EffectPass pass in basicEffect.CurrentTechnique.Passes)
{
   pass.Begin();
   
   pass.End();
}
effect.End();


Manissel681

華夏公益教科書