本文章是由機器翻譯。

Silverlight 3D

在 Silverlight 開發 3D 物件

Rajesh Lal

下載程式碼範例

在本文中,我將示範如何開發 Silverlight 中的 3D 物件。我將簡短的背景上 3D 的開頭,然後查看 [進階的功能在 Silverlight 中,您可以使用,可讓建立和顯示 3D 物件。我將 cube 的簡單的範例,並告訴您不同三種方式可以建立 3D 轉換。我也會說明哪些索引鍵的項目,您必須先在電腦螢幕上顯示 3D 物件。最後,我將探討如何 Silverlight 5 將可讓您回到今天 」 可和建立較豐富的 3D 物件。

Silverlight 支援慣用右手的座標系統,這表示正數 z 軸所指朝向檢視器 (請參閱圖1)。有三個主要元件 3D 旋轉的所需的物件顯示在螢幕上:

  • 檢視方塊
  • 轉換
  • 光的效果


圖 1 參照的 Cube,顯示透視的側邊

透視圖表示的物件越靠近我們的組件出現較遠的。例如,在圖 1、 側邊 bd 看起來大於側邊 fh。在現實生活中的觀點來看會建立一個消失點,這表示當您增加幾行 2 gb aebfcgdh z 軸,它們就會和最上一步是在任意的單一點。

第二個層面就是轉換。當顯示在畫面上,為 3D 物件應該允許往所有方向的 3D 空間中的移動。它可以移動任何單一座標軸中 — 調整大小,同時保持不變的觀點來看。它可以在所有的軸的旋轉 360 度: x、 y 和 z。這可讓 3D 物件需要呈現在螢幕上的彈性。

3D 旋轉的最後一個元素是光的效果。以 3d 效果的光線會建立陰影,這是 [亮光源附近,會以淡的距離。在 3D 轉換,常用的兩種,網底的會是 「 一般 」 的網底及"漸層停駐 」 的網底。我將說明它們之間的差異之後。燈號還要建立陰影上相反的光源的邊。

在推出的範例中,我將探討三種不同的方式,您可以在其中建立 Silverlight 中的 3D 物件:

  • 使用透視圖 3D
  • 使用多個框架和計時器
  • 使用基本項目與 XNA 媒體櫃

第一種方法,建立物件時使用二維的項目,但其外觀和行為,就好像 3D 空間中。透視的 3D 是一種特殊的轉換功能有利於各種基本的轉換,例如旋轉、 縮放以及 3D 空間中的轉譯的 Silverlight 4] 中加入。第二種方法是蠻力法,不要建立 3D 物件,但改建立特定轉換的最後輸出框架和顯示其使用計時器。最後的方法是建立豐富的 3D 物件,XNA 文件庫,將會出現在 Silverlight 5 位元的位元使用基本型別 (矩形的清單)。現在就讓我們開始吧。

建立 Cube 使用的觀點來看 3D

Silverlight 4 支援 PlaneProjection 類別 (請參閱圖 2),可以使用投影屬性的任何 UI 項目,在類別圖表中所示。PlaneProjection 類別也可讓 UI 項目上的觀點來看 3D 轉換。雖然它並不直接允許建立 3D 物件,您就可以使用多個 「 牆壁 」 來建立物件,然後將其轉 3D 空間中。


圖 2 PlaneProjection 類別

PlaneProjection 類別也可讓 LocalOffset 和 GlobalOffset,這用來分別平移大幅度地本身,並在全域的空間中,另一個項目相對於物件。超過 RotationX、 RotationY 和 RotationZ 讓轉動 x 中的項目,y 和 z 軸和 CenterOfRotation 可以相對於項目平面的中心點。

在 [我的範例中,建立"cube",我會建立該 cube 的四個邊,移動其位置 3D 空間中設定 PlaneProjection 屬性,如所示圖 3

[圖 3 設定 PlaneProjection 屬性

<Grid x:Name="LayoutRoot" Background="White" Width="800" Height="700">
  <Rectangle Fill="#9900FF00" Width="250" Height="250" Visibility="Visible">
  <Rectangle.Projection>
    <PlaneProjection x:Name=
      "projectionFront" CenterOfRotationZ="125" RotationX="-180"/>
  </Rectangle.Projection>
</Rectangle>
<Rectangle Fill="#99FF0000" Width="250" Height="250" Visibility="Visible">
  <Rectangle.Projection>
    <PlaneProjection x:Name=
      "projectionBottom" CenterOfRotationZ="125" RotationX="-90" />
  </Rectangle.Projection>
</Rectangle>
<Rectangle Fill="#990000FF" Width="250" Height="250" Visibility="Visible">
  <Rectangle.Projection>
    <PlaneProjection x:Name="projectionBack" CenterOfRotationZ="125" />
  </Rectangle.Projection>
 </Rectangle>
<Rectangle Fill="#99FFFF00" Width="250" Height="250" Visibility="Visible">
  <Rectangle.Projection>
    <PlaneProjection x:Name=
      "projectionTop" CenterOfRotationZ="125" RotationX="90"/>
  </Rectangle.Projection>
</Rectangle>
</Grid>

圖 4,四周會旋轉 90 」,來-90 和-180 度,來建立 cube 的頂端、 底端和最上層投影飛機。125 CenterofRotationZ 值建立的所有飛機可沿著 z 軸旋轉的中心點。


[圖 4 規劃來模擬立體牆邊

一旦建立 cube 時使用投影平面,我需要來旋轉之 x,y 和 z 軸。這是我在 Silverlight 中使用腳本物件的位置。如所示,我會建立三個腳本,一個用於每一個座標軸, 圖 5

[圖 5 旋轉 Cube 使用腳本

<Storyboard x:Name="storyboardRotateX">
  <DoubleAnimation Storyboard.TargetName="projectionFront" 
    Storyboard.TargetProperty="RotationX" From="-180.0" To="180.0" Duration="0:0:10"
    RepeatBehavior="Forever"  />
  <DoubleAnimation Storyboard.TargetName="projectionBottom"
    Storyboard.TargetProperty="RotationX" From="-90.0" To="270.0" Duration="0:0:10"
    RepeatBehavior="Forever"  />
  <DoubleAnimation Storyboard.TargetName="projectionBack" 
    Storyboard.TargetProperty="RotationX" From="0.0" To="360.0" Duration="0:0:10"
    RepeatBehavior="Forever"   />
  <DoubleAnimation Storyboard.TargetName="projectionTop"
    Storyboard.TargetProperty="RotationX" From="90.0" To="450.0" Duration="0:0:10"
    RepeatBehavior="Forever"   />
  </Storyboard>
  <Storyboard x:Name="storyboardRotateY">
  <DoubleAnimation Storyboard.TargetName="projectionFront"
    Storyboard.TargetProperty="RotationY" From="0.0" To="360.0" Duration="0:0:10"
    RepeatBehavior="Forever" />
  <DoubleAnimation Storyboard.TargetName="projectionBottom"
    Storyboard.TargetProperty="RotationY" From="0.0" To="360.0" Duration="0:0:10"
    RepeatBehavior="Forever" />
  <DoubleAnimation Storyboard.TargetName="projectionBack"
    Storyboard.TargetProperty="RotationY" From="0.0" To="360.0" Duration="0:0:10" 
    RepeatBehavior="Forever" />
  <DoubleAnimation Storyboard.TargetName="projectionTop"
    Storyboard.TargetProperty="RotationY" From="0.0" To="360.0" Duration="0:0:10"
    RepeatBehavior="Forever" />
  </Storyboard>
  <Storyboard x:Name="storyboardRotateZ">
  <DoubleAnimation Storyboard.TargetName="projectionFront"
    Storyboard.TargetProperty="RotationZ" From="0.0" To="360.0" Duration="0:0:10"
    RepeatBehavior="Forever" />
  <DoubleAnimation Storyboard.TargetName="projectionBottom"
    Storyboard.TargetProperty="RotationZ" From="0.0" To="360.0" Duration="0:0:10"
    RepeatBehavior="Forever" />
  <DoubleAnimation Storyboard.TargetName="projectionBack"
    Storyboard.TargetProperty="RotationZ" From="0.0" To="360.0" Duration="0:0:10"
    RepeatBehavior="Forever" />
  <DoubleAnimation Storyboard.TargetName="projectionTop"
    Storyboard.TargetProperty="RotationZ" From="0.0" To="360.0" Duration="0:0:10"
    RepeatBehavior="Forever" />
</Storyboard>

在每個圖片敘述,我會旋轉每四個投影飛機,能夠捍衛 cube 結構。請注意,對於 x 軸旋轉,超過 RotationX 值會從原始 RotationX 值平面的開始,而且也會發生另一個的 360 度 ProjectionFront,因此它開始於-180 度,並移至 180 度。您可以看到在圖 6、 cube 可供沿著 x、 y 和 z 軸的旋轉、 可移動任何座標軸和它所支援的側邊的每一個色彩。


[圖 6 Cube,準備好要旋轉

在這個範例中,我是很容易就可以建立一個 cube,然後將其轉而不需大量程式碼的 3D 空間中。這是透視的 3D 轉換的動力。基本 3D 運算而論,您應該使用此選項。不過,它有隨附一些缺點。進階的 3D 物件,投影的飛機數目所需和它們的設定可能會大幅,提高,您必須以手動方式找出每個投影平面和 CenterOfRotation 之間的角度。第二個的問題時 3D 物件的旋轉角度取決於圖片敘述,這樣就可在需要大量 CPU 的 ; 它並不會用於 GPU 呈現物件。使用這個方法的另一個問題是即使找不到,也呈現最後一 cube — 不是最佳的做法。

顯示在螢幕上的 3D 物件所需的第三個主要項目是光的效果。在現實生活中有指示燈,那麼要如何執行您模擬,在螢幕上的 3D 空間中?如前所述,兩個常用的方式,若要這麼做是平面網底及漸層停駐的網底。

平面網底會納入考量平面的表面,並套用以及飛機平均網底。漸層停駐的網底 (洛) 使用漸層加上陰影的表面,並納入考量每一個平面的頂點。與平面不平面加上陰影,但而不是"平滑的"陰影、 根據不同的頂點色彩。

在這個範例中,每個將飛機允許色彩填滿 (單層網底),以及漸層填滿 (漸層陰影),它可以用來模擬光的效果。我將討論這些更新。模擬的光線效果的快速方法是藉由使用下列程式碼之後的 3D 物件的透明度與重疊的放射狀漸層的矩形:

<Rectangle x:Name=
  "BulbGradient" Height="700" Width="800" Margin="0 50 0 0" Grid.Row="1"
  Visibility="Collapsed">
  <Rectangle.Fill>
    <RadialGradientBrush RadiusX="0.5" RadiusY="0.5" GradientOrigin="0.25,0.25">
      <GradientStop Color="#00000000" Offset="0"/>
      <GradientStop Color="#FF000000" Offset="2"/>
     </RadialGradientBrush>
  </Rectangle.Fill>
</Rectangle>

建立 Cube,使用畫面格

建立立體的經驗,第二個方法是使用最後一個畫面格。在這裡,一步是新增 3D 物件本身,但開頭所需的最後輸出並將它匯出為個別的框架。模型化軟體程式的 3D 旋轉的數字可讓您建立 3D 物件,並可以匯出成多個框架,然後匯入到 Silverlight 的轉換。

在這個範例中,我將採用簡單的 cube 動畫,並將它旋轉 x、 y 和 z 軸匯出至多個框架的影像。[圖 7 於 x 軸上顯示的 cube 旋轉八個不同的畫面格。這個範例中,我使用最基本的框架來建立 cube 旋轉,但每秒的多個畫面格建立變得更平穩而完整的旋轉。


[圖 7 八個不同的框架,模擬在 x 軸的旋轉

若要模擬在 Silverlight 中的旋轉,我會使用計時器,如下列程式碼所示:

DispatcherTimer timer = new DispatcherTimer();
timer.Interval = new TimeSpan(0, 0, 0, 0, 500);
timer.Tick += new EventHandler(Tick);
 
private void Tick(object o, EventArgs sender)
{
  string imageuri = "cube/" + axis + "/" + currentImageIndex + ".png";
  bgImage.Source = new BitmapImage(new Uri(imageuri, UriKind.RelativeOrAbsolute));
  if (currentImageIndex <= 8)
    currentImageIndex++;
  else
    currentImageIndex = 1;
}

請注意這是您如何使用這個方法的簡化的版。我在此處使用匯出的影像之故,但許多 3D softwares 允許建立多邊形,使用色彩和漸層,而非圖像,提供不間斷、 變得更平穩的動畫的 XAML 匯出。計時器也會含有動畫的腳本被取代。

這種方法很簡單。您有特定的轉換需求與特定的 3D 物件。您不必擔心建立 3D 物件,以及這項技術可用來建立任何一種 3D 物件,並不會限制您以簡單的 cube。這個方法可用於所有類型的轉換。您可以轉譯、 旋轉和縮放複雜的 3D 物件。甚至會模擬光線,直接從模型化軟體也會將轉譯的效果。

這個方法的主要限制是針對 3D 物件進行程式設計的彈性。一旦您匯出的 3D 物件時,就會產生可能會很難對程式碼的靜態程式碼。Silverlight 應用程式中,您無法移動至其他元素的物件。另一個缺點是呈線性增加所需的框架數目: 使用每個轉換。分開,呈現的作業會對 CPU,所以人質擁有大量畫面格動畫會對效能偷襲。

這會導致使用 XNA 程式庫,在即將推出的 Silverlight 5,會如您所見,能跨越大部分的前兩個方法的問題的第三種方法。但之前,讓我們來討論如何為 3D 物件轉譯在 2D 畫面以數學方式 — 天才背後邏輯 3D。

了解自然、 檢視和投影矩陣

若要顯示的物件,您必須瞭解三個主要概念或"空白"和物件如何從其所屬的物件空間對應至畫面:

  • 世界
  • 檢視
  • 預測

[圖 8 會顯示在此物件會對應至螢幕的順序。


[圖 8] 畫面會對應為 3D 物件的順序

第一 [3D 物件的座標組會是 x、 y 和 z 座標 (也稱為模型空間) 的物件空間中。這些座標是相對於另一個以中心 (0,0,0)。請記住,使用右手邊的笛卡兒座標,正數 z 軸朝向檢視器。

對於立體的 cube,正面的右上角是 (1,1,1),並且背面的左下角會是-1、-1 (-1),如所示圖 9。在物件空間座標是相對於彼此,它們的位置只範圍是從-1 到 + 1。我要使用的物件空間的百分之 75 的 cube,我要乘以每一個協調由.75,因此新的 b將會是 (.75,.75,.75) 與新 g -.75、-.75 (-.75) 將會。


3D 空間中的 [圖 9 座標

當物件放在全局空間時,物件本身並不會移動,但而是由其座標以全局矩陣相乘而得,以對應全局座標相對於。在全局空間中,您可以將座標轉譯物件、 變更來調整大小,變更要將物件旋轉的角度轉換的 3D 物件。全局空間中的物件的座標表示,您需要將全局矩陣的每個頂點位置:

物件的全局座標 = 物件座標 * 全局矩陣

下一個項目是 [相機] 檢視,則表示從其中您正在檢視的物件。這一點可以 3D 空間中變更,而不會變更物件的空間,以及您在全局空間中的實際物件的座標。若要計算鏡頭視野相對於物件的座標,您可以乘以檢視矩陣,使用物件的全局矩陣:

物件檢視座標 = 全局座標 * 檢視矩陣

最後,[物件] 檢視有要呈現在整個螢幕。 這是要計算 [透視圖] 檢視建立,因為之間的距離。到目前為止,我的物件位於 (邊是平行的) 的平行預測,但我要將物件顯示在 [透視圖投影 (邊合併至某個消失點),因此我將物件的檢視矩陣和投影矩陣的全局矩陣的乘積:

物件最後的座標 = 全局座標 * 檢視矩陣 * 投影矩陣

這是在畫面上,這也稱為 WorldViewProjection 的 3D 物件的最終位置。

矩陣

矩陣的結構,為 Microsoft.Xna.Framework,並包含在 Silverlight 5。欄位和方法,以產生轉換矩陣的數字和有 4x4 同質的矩陣,使用 16 的浮點數 (請參閱圖 10)。


[圖 10 A Silverlight 5 的矩陣結構

依資料行 (M11-M33) 的前三個資料列用於時幅] 和 [旋轉轉換,以及用來翻譯的第四列 (M41-M43) (請參閱圖 11)。

[圖 11 4x4 矩陣

M11 M12 M13 M14
M21 M22 M23 M24
M31 M32 M33 M34
M41 M42 M43 M44

若要進一步瞭解矩陣,我們來看它相對於轉換的使用方式。有個矩陣的五種不同類型: 4x4 矩陣結構、 單位矩陣、 轉換矩陣、 小數位數矩陣和旋轉矩陣。

單位矩陣 (請參閱圖 12) 為單位矩陣的大小為 4,且這變成全局空間中的 3D 物件的原始位置。如果您乘以單位矩陣的任何矩陣時,您會得到原始的矩陣,未作任何變更。矩陣結構會提供簡單的屬性會傳回 Matrix.Identity。

[圖 12 單位矩陣

1 0 0 0
0 1 0 0
0 0 1 0
0 0 0 1

若要縮放矩陣物件,提供一個稱為 Matrix.CreateScale 的方法。縮放矩陣會用於縮放變換,在 3D 物件,所以當您多次具有小數位數矩陣的物件 (請參閱圖 13),矩陣會隨之調整大小。

圖 13 縮放矩陣

Sx 0 0 0
0 Sy 0 0
0 0 Sz 0
0 0 0 1

矩陣物件也會提供移動物件在全局空間中的 Matrix.CreateTranslate 方法。乘以轉換矩陣後 (請參閱圖 14),物件會將轉譯的全局空間中。

[圖 14 轉換矩陣

1 0 0 0
0 1 0 0
0 0 1 0
傳送 置入 Tz 1

旋轉、 有多個方法。Matrix.CreateFromYawPitchRoll 方法適用於每個浮動數字值座標軸旋轉。Matrix.CreateRotationX、 Matrix.CreateRotationY 和 Matrix.CreateRotationZ 方法都是循環物件沿著 x、 y 和 z 軸。相對於角度樞紐角度旋轉矩陣中所示,包括 M11 M33 項目圖 15

[圖 15 旋轉矩陣沿 X、 Y 和 z 軸

1 0 0 0
0 Cos 則 θ Sin 則 θ 0
0 -Sin 則 θ Cos 則 θ 0
0 0 0 1
旋轉的 x      
Cos 則 θ 0 Sin 則 θ 0
0 1 0 0
-Sin 則 θ 0 Cos 則 θ 0
0 0 0 1
旋轉 y      
Cos 則 θ Sin 則 θ 0 0
-Sin 則 θ Cos 則 θ 0 0
0 0 1 0
0 0 0 1
旋轉 z      

學習 Silverlight XNA 立體管線

Silverlight 5 XNA 的程式庫提供逐步程序建立 3D 物件,以呈現在螢幕上的頂點座標。這可分為五個主要的步驟 (請參閱圖 16) 包含此處所示的元件:

  1. 頂點緩衝區
  2. WorldViewProjection 座標
  3. 陰影: 頂點、 像素和材質
  4. 圖形處理: 點陣化,多媒體項目和 cull
  5. 最後的輸出: 框架緩衝區


[圖 16 使用 Silverlight 5 XNA 文件庫建立 3D 物件

我將簡短討論每個步驟和其元件一下。

頂點緩衝區在建立頂點緩衝區中頂點集合時,第一個步驟是使用一組頂點建立 3D 物件的基本架構。每個頂點至少包含 x、 y 和 z 座標,但通常也有其他屬性,如 [色彩] 以及 [紋理。這個集合的頂點再用於建立的頂點緩衝區,跳到處理程序的下一個步驟。

WorldViewProjection 座標的最後一個座標計算的頂點與自然、 檢視和投影矩陣相乘而得。物件的關聯,以全局空間、 檢視和投影的計算,並套用。檢查最後兩個區段,如需詳細資訊。一旦您有最終的座標,實際的網底處理程序將會發生。

網底使用頂點網底、 像素網底和材質的網底。而在這個步驟中,第一個頂點著色完成時,然後像素 x 像素的網底。材質的網底也會套用在這個步驟。這些最終的灰色的座標來建立的框架緩衝區。

點陣化,多媒體項目,以及 Cull 期間點陣化影像會轉換為像素,然後再用來移除的 
object,以及隱藏和不可見的圖層的基本架構的多媒體項目和 cull。這是最後會呈現在螢幕上。

框架緩衝區影像點陣化後,被裁掉和 culled,框架緩衝區產生,它會傳送到螢幕顯示。

建立 Cube,使用基本項目

矩陣、 自然、 檢視、 投影和立體管線 Silverlight 5] 所示 XNA 的程式庫的知識,讓我們先建立 3D 的 cube,看看它如何進行。

這種方法最重要的優點是 GPU 加速功能,可讓硬體加速,釋出 CPU 從呈現 3D 物件。此項功能將 EnableGPUAcceleration 參數設定為 true,在 HTML 中用來設定 Silverlight 的外掛程式,如下所示的 Object 標籤內:

<object data="data:application/x-silverlight-2,"
  type="application/x-silverlight-2" width="100%" height="100%">
  <param name="EnableGPUAcceleration" value="true" />
  <param name="source" value="ClientBin/Cube3d.xap"/>
  <param name="minRuntimeVersion" value="5.0.60211.0" />
</object>

在 XAML 中,我將新增在方格中,用來呈現 3D Silverlight 中的 DrawPrimitives 物件的方法 GraphicsDevice 協助的 DrawingSurface 物件 (請參閱圖 17):

<DrawingSurface Loaded="OnLoad" SizeChanged="OnSizeChanged" Draw="OnDraw"/>


[圖 17 DrawPrimitives 類別的方法 GraphicsDevice

我將使用 DrawingSurface 類別中的三種方法來建立及呈現該 cube。OnLoad 方法,用於建立 cube,並初始化所有的著色器,並檢視矩陣,也不會變更此應用程式中。請注意要將 3D 物件中心位於 (0,0,0) (-.75、-.75、-.75) 中使用介於 (.75,.75,.75) 的座標的物件空間的百分之 75。在這裡,我會建立儲存頂點的集合,並初始化 shaderStream、 pixelStream 和 imageStream 將所有用於稍後在 [網底] 步驟中的資料流的頂點緩衝區。我也會初始化檢視矩陣,是一種的角度的相機查看優點在於 cameraPosition 和 cameraTarget 與 Vector3.Up (亦即相機正在尋找往上) 的參數。這段程式碼所示圖 18

[圖 18] 設定 shaderStream、 pixelStream 和 imageStream

VertexBuffer vertexBuffer;
VertexShader vertexShader;
PixelShader pixelShader;
Texture2D texture;
 
private void OnLoad(object sender, RoutedEventArgs e)
{
  vertexBuffer = CreateCube();
  Stream shaderStream = Application.GetResourceStream(new
    Uri(@"Cube3d;component/shader/shader.vs", UriKind.Relative)).Stream;
  vertexShader = VertexShader.FromStream(resourceDevice, shaderStream);
 
  Stream pixelStream = Application.GetResourceStream(new  
    Uri(@"Cube3d;component/shader/shader.ps", UriKind.Relative)).Stream;
  pixelShader = PixelShader.FromStream(resourceDevice, pixelStream);
           
  Stream imageStream = Application.GetResourceStream(new
    Uri(@"Cube3d;component/scene.jpg",
    UriKind.Relative)).Stream;
  var image = new BitmapImage();
  image.SetSource(imageStream);
  texture = new Texture2D(resourceDevice, image.PixelWidth,
    image.PixelHeight, false, SurfaceFormat.Color);
  image.CopyTo(texture);
        
  Vector3 cameraPosition = new Vector3(0, 0, 5.0f);
  Vector3 cameraTarget = Vector3.Zero; 
  view = Matrix.CreateLookAt(cameraPosition, cameraTarget, Vector3.Up);
}

下一步是建立 3D cube 的頂點緩衝區。如所示,我將建立一個 CreateCube 方法圖 19 ,會傳回 VertexBuffer。我會構成 cube 和 EFGH 的最後一 cube ABCD 3D 空間中,建立兩個矩形。我可以使用 VertexPositionColor 結構來建立一系列的頂點和每一項相關聯的色彩。

建立 VertexBuffer 的 CreateCube 方法中的 [圖 19

VertexBuffer CreateCube()
{
  var vertexCollection = new VertexPositionColor[36];
 
  // Front coordinates
  Vector3 cubeA = new Vector3(-0.75f, 0.75f, 0.75f);
  Vector3 cubeB = new Vector3(0.75f, 0.75f, 0.75f);
  Vector3 cubeC = new Vector3(-0.75f, -0.75f, 0.75f);
  Vector3 cubeD = new Vector3(0.75f, -0.75f, 0.75f);
  // Back coordinates
  Vector3 cubeE = new Vector3(-0.75f, 0.75f, -0.75f);
  Vector3 cubeF = new Vector3(0.75f, 0.75f, -0.75f);
  Vector3 cubeG = new Vector3(-0.75f, -0.75f, -0.75f);
  Vector3 cubeH = new Vector3(0.75f, -0.75f, -0.75f);
 
  // Colors
  Color cRed = Color.FromNonPremultiplied(255, 0, 0, 156);
  Color cGreen = Color.FromNonPremultiplied(0, 255, 0, 156);
  Color cBlue = Color.FromNonPremultiplied(0, 0, 255, 156);
  Color cYellow = Color.FromNonPremultiplied(255, 255, 0, 156);
  Color cBlack = Color.FromNonPremultiplied(0, 0, 0, 156);
  Color cWhite = Color.FromNonPremultiplied(255, 255, 255, 156);
 
  // Front
  vertexCollection[0] = new VertexPositionColor(cubeA, cGreen);
  vertexCollection[1] = new VertexPositionColor(cubeB, cGreen);
  vertexCollection[2] = new VertexPositionColor(cubeC, cGreen);
  vertexCollection[3] = new VertexPositionColor(cubeB, cBlue);
  vertexCollection[4] = new VertexPositionColor(cubeD, cBlue);
  vertexCollection[5] = new VertexPositionColor(cubeC, cBlue);
  // Back 
  vertexCollection[6] = new VertexPositionColor(cubeG, cBlue);
  vertexCollection[7] = new VertexPositionColor(cubeF, cBlue);
  vertexCollection[8] = new VertexPositionColor(cubeE, cBlue);
  vertexCollection[9] = new VertexPositionColor(cubeH, cGreen);
  vertexCollection[10] = new VertexPositionColor(cubeF, cGreen);
  vertexCollection[11] = new VertexPositionColor(cubeG, cGreen);
  // Top
  vertexCollection[12] = new VertexPositionColor(cubeE, cRed);
  vertexCollection[13] = new VertexPositionColor(cubeF, cRed);
  vertexCollection[14] = new VertexPositionColor(cubeA, cRed);
  vertexCollection[15] = new VertexPositionColor(cubeF, cYellow);
  vertexCollection[16] = new VertexPositionColor(cubeB, cYellow);
  vertexCollection[17] = new VertexPositionColor(cubeA, cYellow);
  // Bottom 
  vertexCollection[18] = new VertexPositionColor(cubeH, cRed);
  vertexCollection[19] = new VertexPositionColor(cubeG, cRed);
  vertexCollection[20] = new VertexPositionColor(cubeC, cRed);
  vertexCollection[21] = new VertexPositionColor(cubeD, cYellow);
  vertexCollection[22] = new VertexPositionColor(cubeH, cYellow);
  vertexCollection[23] = new VertexPositionColor(cubeC, cYellow);
  // Left
  vertexCollection[24] = new VertexPositionColor(cubeC, cBlack);
  vertexCollection[25] = new VertexPositionColor(cubeG, cBlack);
  vertexCollection[26] = new VertexPositionColor(cubeA, cBlack);
  vertexCollection[27] = new VertexPositionColor(cubeA, cWhite);
  vertexCollection[28] = new VertexPositionColor(cubeG, cWhite);
  vertexCollection[29] = new VertexPositionColor(cubeE, cWhite);
  // Right 
  vertexCollection[30] = new VertexPositionColor(cubeH, cWhite);
  vertexCollection[31] = new VertexPositionColor(cubeD, cWhite);
  vertexCollection[32] = new VertexPositionColor(cubeB, cWhite);
  vertexCollection[33] = new VertexPositionColor(cubeH, cBlack);
  vertexCollection[34] = new VertexPositionColor(cubeB, cBlack);
  vertexCollection[35] = new VertexPositionColor(cubeF, cBlack);
 
  var vb = new VertexBuffer(resourceDevice,
    VertexPositionColor.VertexDeclaration,
    vertexCollection.Length, BufferUsage.WriteOnly);
  vb.SetData(0, vertexCollection, 0, vertexCollection.Length, 0);
  return vb;
}

在繪圖介面的 OnSizeChanged 方法可用來更新投影及長寬比表面的維度所根據的畫面:

private void OnSizeChanged(object sender, SizeChangedEventArgs e)
{
  DrawingSurface surface = sender as DrawingSurface;
  float sceneAspectRatio = (float)surface.ActualWidth / (float)surface.ActualHeight
  projection = Matrix.CreatePerspectiveFieldOfView
               (MathHelper.PiOver4, sceneAspectRatio, 1.0f, 100.0f);
}

最後一個方法是 OnDraw,可用來在 3D 的 cube 上的動態轉換。 這是我用來套用 Matrix.CreateScale,若要調整大小的 cube,若要旋轉的 Matrix.CreateFromYawPitchRoll 和 Matrix.CreateTranslate,將它移。 WorldViewProjection 矩陣的計算方式在這裡,並且傳送到的 vertexShader 方法網底頂點,它會依序將以灰色顯示 cube 的 「 牆壁 」 的 pixelShader。 這又可以傳送到 textureShader,可以為基礎的映像。 最後,GraphicDevice 物件呼叫 DrawPrimitives 方法來呈現輸出的框架中,如所示圖 20

圖 20 呼叫 DrawPrimitives 方法來呈現輸出的框架

void OnDraw(object sender, DrawEventArgs args)
{
  Matrix position = Matrix.Identity;
  Matrix scale = Matrix.CreateScale(1.0f);
  float xf = 0.0f; float yf = 0.0f; float zf = 0.0f;
 
  if (cubeXAxis) xf = MathHelper.PiOver4 * (float)args.TotalTime.TotalSeconds;
  if (cubeYAxis) yf = MathHelper.PiOver4 * (float)args.TotalTime.TotalSeconds;
  if (cubeZAxis) zf = MathHelper.PiOver4 * (float)args.TotalTime.TotalSeconds;
 
  Matrix rotation = Matrix.CreateFromYawPitchRoll(xf, yf, zf);
  Matrix world;
  if (translateZ != 0)
    world = rotation * Matrix.CreateTranslation(0, 0, (float)translateZ);
  else
    world = scale * rotation * position;
           
  // Calculate the final coordinates to pass to the shader.
Matrix worldViewProjection = world * view * projection;
  args.GraphicsDevice.Clear(ClearOptions.Target | ClearOptions.DepthBuffer,
    new Microsoft.Xna.Framework.Color(0, 0, 0, 0), 10.0f, 0);
  
  // Set up vertex pipeline.
args.GraphicsDevice.SetVertexBuffer(vertexBuffer);
  args.GraphicsDevice.SetVertexShader(vertexShader);
  args.GraphicsDevice.SetVertexShaderConstantFloat4(0, ref worldViewProjection);
 
  // Set up pixel pipeline.
args.GraphicsDevice.SetPixelShader(pixelShader);
  args.GraphicsDevice.Textures[0] = texture;
  args.GraphicsDevice.DrawPrimitives(PrimitiveType.TriangleList, 0, 12);
  args.InvalidateSurface();
}

此動態呈現 3D 的最後一個 cube,在繪圖介面上 (請參閱圖 21)。


圖 21 繪圖介面上的最後一個 3D Cube

這種方法使用 GPU 加速,並不會被轉換為隱藏的 3D 物件的側邊或背面,與不同的是我第一種方法。這也使用框架緩衝區在記憶體中呈現類似第二種方法中,但具有完整控制權和彈性的程式設計物件,最後的輸出和 Gdi 物件一樣。

您現在已經學會三種不同的方式,以建立 3D 物件在 Silverlight 中,每一個都有它自己的優點和缺點。當您嘗試在 Silverlight 的 3D 世界應該屬於有用加入您的工具箱的項目。

Rajesh Lal  是熱衷於 Silverlight 和 Web 技術。他已經撰寫多個活頁簿視窗的小工具、 Web widget 和行動的 Web 技術,包括 「 即將來臨的書,更 「 有趣的 Silverlight 4 」 (CreateSpace,2011年)。如需詳細資訊,請連絡 mconnectrajesh@hotmail.com 或造訪 silverlightfun.com

因為有到下列的技術專家來檢閱這份文件:Marc Clifton, Rick KingslanJosh Smith