Direct3D世界的Hello:高洛德渲染的三角形(轉)

post0發表於2007-08-12
Direct3D世界的Hello:高洛德渲染的三角形(轉)[@more@]

  Direct3D的所有操作都是在Direct3D裝置上進行的。建立裝置要使用Device類的建構函式。

  

  public Device ( System.Int32 adapter , Microsoft.DirectX.Direct3D.DeviceType deviceType ,

  

  System.Windows.Forms.Control renderWindow , Microsoft.DirectX.Direct3D.CreateFlags

  

  behaviorFlags, Microsoft.DirectX.Direct3D.PresentParameters presentationParameters )

  

  Direct3D裝置有3種型別,Hardware, Software和Reference型別。其中Reference型別只有在DirectX SDK中的runtime才有。我們通常使用的DirectX Runtime沒有這種型別的裝置。

  

  建立裝置的時候我們還要指定在那個顯示卡上進行操作,一般情況下,我們指定0,表示使用預設的顯示卡。

  

  我們還要把該裝置與一個視窗關聯起來。

  

  CreateFlags中我們經常使用的引數,就是指定要使用硬體/軟體定點處理。

  

  當我們縮放視窗的時候,拉大視窗肯定會導致視窗被重繪,但是縮小視窗並不能導致視窗被繪製。這也是合情合理的,因為縮小視窗只是導致視窗可見面積的減小,

  

  沒有必要重新繪製。當然這是一般情況,在我們的例子,我們要求三角形總是在視窗中央,所以這樣的表現不被接受。我們需要自己來強制重繪,所以,我們在Form1_Paint()事件中加入了this.Invalidate()函式。

  

  但是這樣又引入了另外的問題,執行修改後的程式後,我們發現我們繪製的內容不見了。這是因為在Windows Form中不是所有的繪製都發生在Paint中,因為Windows XP支援透明視窗,這個透明處理是由作業系統自動完成的,這就說明在Paint事件之外會發生一些繪製操作,Windows會用預設的資訊來重繪視窗,這導致我們繪製的內容被覆蓋掉。這就是為什麼我們要在我們的Form類的建構函式種使用SetStyle函式了。

  

  (??為什麼加入了Invalidate()後會出現這種現象?

  

  我的看法,不對的地方還請高手指教了。嘿嘿。

  

  我們是先Render,然後才呼叫Invalidate()的。Invalidate()觸發Windows的Piant外繪製,我們的繪製被覆蓋。移動的時候,反覆這樣清除,導致閃爍,所以

  

  就有了我們看到的現象。

  

  )

  

  this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.Opaque, true);

  

  這一語句告訴Windows 我們的視窗不是透明的,並要求所有的繪製都要發生在Paint事件中。否則,我們會發現我們繪製的內容根本看不到,當移動視窗的時候,會隱隱看到我們繪製的三角形在閃爍。大家可以試試看,在我的機器上,不是很明顯。

  

  在繪製三角形的時候,我們使用了TransfomredColored的頂點格式。這種格式就如他的名字一樣,是已經轉換過的,對於這種格式的頂點,Direct3D不再做Lighting, View Transform,Projection Transform.我們在資料中指定的座標值就是這些頂點要顯示的地方。

  

  而且我們可以看到,繪製出來的三角形內部已經被填充了,可是我們並沒有做任何類似的操作。這其實是一種渲染模式(Shading Mode, 不知道這麼翻譯對不對)。

  

  高洛德(Gouraud)渲染,還有Flat Shading Mode和Phong Shading Mode.

  

  (Gouraud渲染很有用的,尤其是在用多個三角形描述一個曲面/物件的時候,面對幾千個上萬個三角形,你不可能自己去計算每個三角形的光照吧。Gouraud可以根據每個三角形面和光源的角度自動計算這些三角形頂點的具體顏色和亮度,然後用差值平滑處理三角形內部的點,這樣就可以形成真實的物體了。具體來說是這樣,

  

  每個三角形是是一個平面,有自己的法向向量,光線也有自己的向量,根據這個三角形面和光線的角度可以計算出這個三角形的頂點的具體光照情況,然後根據材料特性就可以計算出這些頂點的具體顏色、亮度等。然後對於三角形內部的其他點,可以用差值平滑處理,計算出這些點的顏色和亮度資訊。具體怎麼算,我也不知道,呵呵,

  

  我不是搞演算法的,不關心這個。

  

  如果你把頂點資料的格式改成 PositonColored,你會發現,又看不到我們繪製的東西了,呵呵,我們下面會知道為什麼。

  

  好了,下面是這節的例子。

  

  #region Using directives

  

  using System;

  

  using System.Collections.Generic;

  

  using System.ComponentModel;

  

  using System.Data;

  

  using System.Drawing;

  

  using System.Windows.Forms;

  

  using Microsoft.DirectX;

  

  using Microsoft.DirectX.Direct3D;

  

  #endregion

  

  namespace Ch01

  

  {

  

  partial class Form1 : Form

  

  {

  

  private Device device = null;

  

  public Form1()

  

  {

  

  InitializeComponent();

  

  this.SetStyle( ControlStyles.AllPaintingInWmPaint | ControlStyles.Opaque, true );

  

  }

  

  public void InitializeGraphics()

  

  {

  

  PresentParameters para = new PresentParameters();

  

  para.Windowed = true;

  

  para.SwapEffect = SwapEffect.Discard;

  

  device = new Device( 0, DeviceType.Hardware, this, CreateFlags.HardwareVertexProcessing, para );

  

  }

  

  private void Form1_Paint(object sender, PaintEventArgs e)

  

  {

  

  device.Clear( ClearFlags.Target, Color.Blue, 1.0f, 0 );

  

  CustomVertex.TransformedColored[] verts = new CustomVertex.TransformedColored[3];

  

  verts[0].Position = new Vector4( this.Width / 2.0f, 50.0f, 0.5f, 1.0f );

  

  verts[0].Color = Color.Aqua.ToArgb();

  

  verts[1].Position = new Vector4( this.Width - this.Width / 5.0f, this.Height - ( this.Height / 5.0f ), 0.5f, 1.0f );

  

  verts[1].Color = Color.Black.ToArgb();

  

  verts[2].Position = new Vector4( this.Width / 5.0f, this.Height - ( this.Height / 5.0f ), 0.5f, 1.0f );

  

  verts[2].Color = Color.Purple.ToArgb();

  

  device.BeginScene();

  

  device.VertexFormat = CustomVertex .TransformedColored .Format;

  

  device.DrawUserPrimitives( PrimitiveType.TriangleList, 1, verts );

  

  device.EndScene();

  

  device.Present();

  

  this.Invalidate();

  

  }

  

  static void Main()

  

  {

  

  using ( Form1 frm = new Form1() )

  

  {

  

  // Show our form and initialize our graphics engine

  

  frm.Show();

  

  frm.InitializeGraphics();

  

  Application.Run( frm );

  

  }

  

  }

  

  }

  

  }

  

  BeginScene()和EndScene()是配對的。必需呼叫Present()函式後,我們繪製的東西才能顯示出來。

  

  DrawUserPrimitives()是具體的繪製函式,還有個類似的函式DrawPrimitives(),這個函式用起來就比較麻煩了,得建立頂點緩衝,然後透過Device的SetStreamSource()函式設定到裝置上去,並且要把頂點資料,寫到這個頂點緩衝中去,然後才能夠呼叫DrawPrimitives()繪製。

  

  在使用DrawUserPrimitives()的時候,我們使用了PrimitiveType.TrangleList,這裡涉及了一個基本概念,3D Primitive.在Direct3D中支援多種型別。

  

  其中比較重要的是,Triangle List, Triangle Strip, Triangle Fan.具體概念,可以參考其他的資料。並且這些Primitive在不同的Shading Mode中的含義也是不同的,起碼在Gouraud shading mode中是這樣,TriangleList的每個三角形被單獨平滑處理,TriangleStrip中的三角形被作為一個整體來平滑,TriangleFan呢?

  

  也應該和TriangleStrip一樣吧,沒有試過,有心人給確認一下吧。

  

  而且組織頂點資料的時候還涉及了Backface Culling的概念,也就是說頂點的順序也是有講究的,不能亂來。否則,打死你也看不到你畫的東西,除非你在device.RenderState.CullMode中關掉了Backface Culling

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/8225414/viewspace-952141/,如需轉載,請註明出處,否則將追究法律責任。

相關文章