影像處理---深入ManagedDirectX9(3)(轉)

post0發表於2007-08-12
影像處理---深入ManagedDirectX9(3)(轉)[@more@]

  拖放視窗時自動重置Device

  任何曾經使用C++或VB開發DirectX3D的人都知道,在改變視窗大小時,需要重新設定devicd,否則,DirectX3D會按原來的解析度繼續渲染場景,並且把結果複製到(透過拉伸)新的視窗。當透過Windows Form控制元件建立device時,聰明的Mamaged DirectX能發現你改變了視窗的大小,並且重置device。毫無疑問,程式總是能在正常的行為下執行,同時,你也能方便的自己重置device。在自動重置device之前,會引發一個叫做DeviceResizing的事件。捕獲這個事件,把EventArgs類的Cancel成員設定為true,就能回到預設的行為,在建立device之後加上如下程式碼

  private void CancelResize(object sender, CancelEventArgs e)

  {

  e.Cancel = true;

  }

  如你所見,這個方法只是簡單的say yes,我們確實想要取消這個操作。現在訂閱事件處理程式,讓device知道不進行這種操作 :

  device.DeviceResizing += new CancelEventHandler(this.CancelResize);(注:CancelEventHandle委託在System.ComponentModel名稱空間)

  執行程式,最大化視窗。三角的位置還和原來一樣,不過這次看起來可怕極了。邊緣都是鋸齒,看起來糟糕透了。可以刪除我們剛新增的程式碼了。Managed DirectX預設操作已經幫我們完成了這個任務,可以直接利用它。

  我說:“要有光”,於是場景就有了光

  我們繪製了三角形並且讓他轉起來了,怎樣才能讓他更好呢?當然是燈光。在前面曾簡要的提到過它,事實上,那個時候我們完全關閉了燈光。首先要做的就是先回到那個黑暗的場景:

  deviceRenderState.Lighting = true;

  其實你甚至可以把整行都刪了,device的預設行為是開啟燈光的;只是為了讓程式碼更清楚才保留它。現在獲得了一個黑色的旋轉三角。或許我們應該先定義一盞燈,再來開啟它。你可能已經注意到有一個燈光陣列連線到了device類上,並且這個陣列的每一個元素都儲存了有關燈光的大量屬性。我們希望定義場景裡的第一盞燈並且開啟它,So,在OnPaint方法定義了三角形之後的地方(注:與sdk中有區別,不過都是一樣的效果^_^)新增如下程式碼:

  device.Lights[0].Type = LightType.Point;

  device.Lights[0].Positon = new Vector3();

  device.Lights[0].Diffuse = System.Drawing.Color.White;

  device.Lights[0].Attenuation = 0.2f;

  device.Lights[0].Range = 1000.0f;

  device.Lights[0].Commit();

  device.Lights[0].Enabled = true;

  這些程式碼什麼意思呢?首先申明瞭要建立的燈光型別,我們選擇了一個在所有方向上輻射強度都一樣的point light,創造了一個燈泡般的世界。當然,也有燈光沿著指定方向傳播的direction light。direction light只會產生方向和顏色上的效果,忽略其他的燈光要素(比如光線的削弱(attenuation)和範圍(range)),因此它也是計算量最小的燈光。最後一種能用的就是spot light了,類似於劇場裡用來照亮舞臺上人物的燈光。有許多的要素來描述spot light(位置,方向,角度,等等),所以它是系統裡所需計算量最大的燈光。

  在對燈光型別簡單的討論之後,我們繼續。接下來設定燈光的位置。因為三角形的中心在(0,0,0),所以我們把燈光也放到那個位置。Vector3無引數的建構函式完成了這個任務。把燈光的漫射顏色設定為白色,這樣可以正常的照亮表面。接下來設定控制燈光強度在空間改變的削弱屬性。範圍是燈光能產生效果的最遠距離。例子裡的範圍已經遠遠超過了我們所需要的。請查閱sdk尋找有關燈光的更多內容。

  最後我們把燈光提交給了device,並使它可用。如果你瀏覽燈光的屬性,會注意到一個叫做“Deferred”的布林值。預設情況下,這個值是false,所以你需要在準備使用燈光之前呼叫Commit函式。把這個值設為true,可以取消對Commit的呼叫,但會帶來一定的效能損失。在觀看燈光的效果前一定要確定它是enable和committed的。

  回到程式,你發現即使我們為場景定義了燈光,三角也還是黑色的!開啟了燈,卻看不到光,Direct3D一定沒有照亮我們的三角形,事實上,它確實沒有。只有在幾何體的每一個面都有一條法線(normal)時,才會進行燈光的計算。知道了這點,我們來為三角新增法線吧,這樣就能在場景裡看到它了。最簡單的方法就是把頂點格式改為一種包含了法線的格式。碰巧我們也有這樣一個結構了,改變建立三角形的程式碼:

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

  verts[0].SetPositon(new Vector3(0.0f,1.0f,1.0f));

  verts[0].SetNormal(new Vector3(0.0f,0.0f,-1.0f));

  verts[0].Color = Ststem.Drawing.Color.White.ToArgb();

  verts[1]``````

  `````````

  更新頂點格式來適應新的資料:

  device.VertexFormat = CustomVertex.PositionNormalColored.Format;

  這次最大的改變就是使用了一組包含法線的資料,並且把三角形的顏色改為白色。可以看到,我們把垂直於頂點指向外的方向定義為法向量。因為點只是在Z平面內移動,所以沿著Z軸的負方向即是法線向量的方向。現在程式就一切正常了。可以試著改變一下燈光的漫射顏色,看看會有怎樣的變化。

  還有一件應該記住的事:燈光是按照每一個頂點來計算,所以在low polygon模型(就像我們簡單的三角形)的情況下,燈光可能會不太真實。我們會在後邊的章節裡討論一些高階燈光技術,比如per pixel linghting。這些燈光能創造一個真實的世界。

  Device State and Transforms

  至今為止,示例程式碼裡還有兩項沒有討論過:裝置狀態(device state)以及變換(transform)。對一個裝置來說,有三種不同方式的裝置狀態:the render state,The sampler states,和 the texture state。我們僅僅使用過the render state中的幾種型別;後邊的兩種型別是用來處理紋理的。不要擔心我們很快就會談到紋理。The render state類規定了DirectX3D怎樣來對場景進行光柵化。可以使用這個類來改變很多屬性,包括我們已經使用過的燈光以及剔除。其他render state可用的選項有填充模式(fill mode) (比如wire frame mode)和各種霧化引數。我們也會來接下來的幾章深入討論。

  前面提到過,變換就是用來把幾何體位置從一個座標系轉到另一個座標系的一系列矩陣。用於device上的三個主要變換就是world,view以及projection變換,但是也有一些其他的變換。比如用來控制texture stages的變換,就依賴於一個255的世界矩陣(There are transforms thst are used to modify texture stages,as well as up to 255 world matrices??).

  Swapchains and RenderTargets

  Device到底作了些什麼工作來繪製這些三角形呢?device有一些固定的方法來處理在哪繪製並且如何繪製物件。每一個device都有一個交換鏈(swap chain)以及一個渲染目標(render target)。

  一條交換鏈實際上就是一系列被控制著用來渲染的緩衝區。所有繪圖過程都是在交換鏈中的後備緩衝區發生。當使用SwapEffect.Flip來建立一條交換鏈時,後備緩衝區翻轉(flipped)為真正被圖形卡用於讀取資料的前緩衝(front buffer)。同時,三號緩衝區變為新的後備緩衝,而先前的前緩衝變為未使用過的三號緩衝區。

  真正的翻轉操作是透過改變圖形卡當前所讀的資料區、剛讀過的資料區以及後備緩衝區之間的地址來實現。只有在全屏模式下,才會發生真正的翻轉操作。而在視窗模式,翻轉實際上只是資料的複製而已,因為device並沒有控制著整個顯示器,僅僅是一小部分而已。雖然兩種模式下結果都一樣。全屏模式下,有一些驅動程式也會使用翻轉操作來實現SwapEffect.Discard 或者 SwapEffect.Copy。

  如果使用SwapEffect.Copy或SwapEffect.Flip來建立交換鏈,可以確保presen()之後不會影響後備緩衝中的內容。執行時會在需要時強制建立額外的隱藏緩衝。建議使用SwapEffect.Discard來避免這種潛在的損失。這種模式允許驅動程式選擇最高效的方法分配後備緩衝。使用SwapEffect.Discard時,不值得(???)在繪製新的圖形前檢查你是否清除了整個後備緩衝。除錯模式下的執行時將會使用隨機的資料來填充(剛剛使用過的)後備緩衝,讓開發者檢查是否忘了呼叫clear()。(it is worth nothing that when usuing SwapEffect.Discardyou will want to ensure that you clear the entire back buffer before starting new drawing operations. the runtime will fill the the back buffer with random data in the debug runtime so developers can see if they forget to call clear)(注:這一段內容看的不是太明白,所以把原文也給出來。Sdk中對SwapEffect列舉的解釋也不是太清除。參考sdk:交換效果明確定義了呼叫present()之後,後備緩衝的狀態。Flip交換鏈是一個迴圈的佇列,可以有0~(n-1)塊後備緩衝, discard交換鏈是一個佇列, copy交換鏈只有一塊後備緩衝。Flip中的後備緩衝在present()之後內容不會改變,所以系統需要額外記憶體作為後備緩衝,帶來效能損失。既然後備緩衝中的內容不改變,如何構成迴圈佇列來使用?? Discard後備緩衝中佇列的長度以及怎樣變化也沒有明確說明,只有“The swap chain is essentially a queue where 0 always indexes the back buffer that will be displayed by the next Present operation and from which buffers are discarded once they have been displayed. An application that uses this swap effect should update an entire back buffer before invoking a Present operation that displays it.The debug version of the runtime overwrites the contents of discarded back buffers with random data, to enable developers to verify that their applications are updating the ent

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

相關文章