?? 9-3.txt
字號:
拖放窗口時自動重置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 entire back buffer surface correctly.” 隨機數據能幫助檢查是否更新了整個后備緩沖區??既然會丟棄數據還需要調用clear??)
交換鏈的后備緩沖區也同樣能作為渲染目標。毫無疑問,當創建了device,創建了交換鏈之后,渲染目標就被設置為鏈的后備緩沖。一個渲染目標就是能保存所執行的繪制任務的輸出的表面(a surface that will hold the output of the drawing operations that you perform)。如果你創建了多個交換鏈的話,就必須確定預先更新了device的渲染目標。后邊我們會稍后討論這點。
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -