Direct3D提供了一套強有力的工具來幫助我們提高三維場景的逼真度。下面我們將討論一些Direct3D產生的通用的特殊效果。但是,可能出現的效果並不只限於這裏所討論的內容。下面我們分成幾個主要的部分:
1. 霧化(Fog)2. 公告板技術(Billboarding)3. 雲、煙與蒸汽效果(Clouds, Smoke, and Vapor Trails)4. 紋理融合技術(Texture Blending Techniques)5. 火焰、閃光、爆炸效果等(Fire, Flares, Explosions, and More)6. 運動模糊(Motion Blur)7. 模板緩衝技術(tencil Buffer Techniques)8. 彩色光(Colored Lights)9. 反走樣(Antialiasing)
下面我們介紹有關霧化的內容以及不同霧化特性的使用:
1.1 霧化介紹1.2 霧化公式1.3 霧化融合1.4 霧化顏色1.5 基於範圍的霧化1.6 像素霧化(Pixel Fog)1.7 頂點霧化(Vertex Fog)
在一個三維場景中使用霧化效果可以增加真實感、產生一定的氣氛或者設置一種情緒,也可以減少由於遠處的幾何體靠近觀察者所產生的失真。Direct3D立即模式支持兩種霧化模型,像素霧化和頂點霧化,它們都有各自的特點和編程接口。
從本質上來說,霧化就是將場景中對象的顏色與根據對象深度或者離觀察的距離而選擇的一個霧的顏色進行混合而得到的。當對象的距離不斷增加時,它原始的顏色也就更多的融合到霧的顏色中,這樣就好像對象在逐漸的模糊。下面的插圖中的一幅沒有使用霧化,而另一幅使用了霧化。
上圖中,左邊的場景有清晰的地平線,場景中所有的風景都是可見的。右邊的場景使用了霧化效果,霧的顏色預備將的顏色一樣,這樣就使物體逐漸的消失在背景中。如果結合使用分立的(discrete)霧化效果,那麼就可以在場景中添加一定的情緒,使場景中對象的顏色變的柔和。
Direct3D提供了兩種產生霧化的方法,像素霧化和頂點霧化,它們是根據產生霧的方式來命名的。
通過改變Direct3D計算霧化的方式,我們可以控制如何對對象進行霧化處理。D3DFOGMODE枚舉類型中包含了用來確定三種霧化公式的成員。所有計算霧化因子的公式都是距離的函數。如何計算距離也要根據投影矩陣或者Range-Based霧化是否有效而定。
在線性公式中,start是霧化效果開始的距離,end是霧化結束的距離,d表示深度(或到觀察點的距離)。d值的增加就表示對象在不斷遠離。像素霧化和頂點霧化都支持線性公式,但是目前只有像素霧化才支持指數公式:
f =1 / ed×density
上面的兩個公式中,e表示自然對數的低(它的值約爲2.71828),density表示任意的霧化密度,範圍從0.0到1.0,d表示深度(或離觀察點的距離)。
注:系統將霧化因子存儲在頂點鏡面顏色的alpha乘分鐘。如果程序執行自己的變換和光線處理,可以手控的插入霧化因子值。
下圖中描繪出了上述公式的曲線圖:
Direct3D計算霧化效果時,就會使用上述公式中的一個來計算霧化因子,再應用到下面的公式中:
C=f·Ci+(1-f)·Cf
公式中,使用霧化因子f對當前多邊形的顏色Ci進行縮放,然後再和霧的顏色Cf與(1-f)的乘積相加。得到的顏色值就是霧的顏色與原始顏色的混合值。這個公式適用於DirectX 6.0中支持的所有設備。對於遺留的ramp設備,霧化因子對漫射和鏡面顏色成分進行縮放,範圍也在0.0到1.0之間(包括它們)。通常,近處平面的霧化因子爲1.0,遠處的爲0.0。
霧化融合操作是指將霧化因子應用到霧和對象的顏色,從而產生最終表現在場景中的顏色。D3DRENDERSTATE_FOGENABLE渲染狀態控制霧化融合。將它設置爲TRUE表示霧化融合有效,缺省時爲FALSE。
|
像素霧化和頂點霧化的霧化顏色都通過D3DRENDERSTATE_FOGCOLOR渲染狀態來設置。這個值可以是任意的RGB顏色,它以RGBA方式來聲明,alpha成分被忽略了。
下面例子中將霧化顏色設置爲白色:
HRESULT hr; |
有時,使用霧化可能會產生圖形失真,會使對象以非直觀的方式來對霧化顏色進行渲染。例如,一個場景中有兩個可見的對象,其中一個的距離可以使用霧化效果,而另一個則距離較近,不受霧化的影響。這時,如果觀察區域發生了旋轉,那麼所表現出來的霧化效果就會改變,即使場景中的對象並沒有移動。下圖向我們描繪出了這樣的情況:
基於範圍的霧化是又一種決定霧化效果的方法,並且它更加精確。在這種方式中,Direct3D使用視點到頂點的真實距離來計算霧化效果,並隨着兩點間距離的增加,逐漸增加霧化效果,而不是根據頂點在場景中的深度變化,這樣就能避免旋轉產生的失真。
如果當前設備支持基於範圍的霧化,那麼可以調用IDirect3DDevice3::GetCaps方法在D3DPRIMCAPS結構的dwRasterCaps成員中設置D3DPRASTERCAPS_FOGRANGE能力標誌。要使基於範圍的霧化有效,要將D3DRENDERSTATE_RANGEFOGENABLE渲染狀態設置爲TRUE。
Direct3D在變換和光線處理時計算基於範圍的霧化。程序不使用Direct3D的變化和光線處理引擎時,也可以執行自己的頂點霧化計算。這時,要爲每個頂點的鏡面成分的alpha成分提供基於範圍的霧化因子。
注:目前,還沒有硬件支持每像素的基於範圍的霧化(per-pixel range-based fog)。因此,只有在使用Direct3D的變換和光線處理引擎進行頂點霧化時,才能使用基於範圍的霧化。如果你的程序執行自己的變換和光線處理,那麼也必須執行自己的霧化計算。
這一部分介紹像素霧化的內容:
1.6.1 關於像素霧化1.6.2 目相關對基於Z深度(Eye-Relative vs. Z-Based Depth)1.6.3 像素霧化參數1.6.4 使用像素霧化
像素霧化的名稱來源於它的處理方式,它是通過計算每個像素來得到霧化效果的。(這與頂點霧化不同,Direct3D在執行變化和光線處理時使用頂點霧化方式。)像素霧化有時也被稱爲“表格霧化”,因爲一些設備要使用一個預先計算好的查詢表格來決定霧化因子(使用每個像素的深度)。像素霧化可以使用任何D3DFOGMODE枚舉類型的成員聲明的霧化公式。像素霧化公式的指向是根據驅動器而定的,如果驅動器不支持複雜的霧化公式,那它就只能使用較簡單的公式來進行計算了。
注:我們在“基於範圍的霧化”中討論過,像素霧化不支持基於範圍的霧化運算。
爲了減少由於深度緩衝中z-值的不均勻分佈引起的有關霧化的圖形失真,大多數硬件設備都使用了目相關(eye-relative)深度來代替基於z的深度值。目相關深度從本質上來說是一個同質座標系統中的w成分。(Direct3D通過一個設備空間座標設置的RHW成分的倒數來產生真正的w。)如果設備支持目相關霧化,那麼當你調用IDirect3DDevice3::GetCaps方法時,就要在D3DPRIMCAPS的dwRasterCaps成員中設置D3DPRASTERCAPS_WFOG標誌。(傳遞給GetCaps的D3DDEVICEDESC結構包含了多個D3DPRIMCAPS結構來描述對於不同類型圖元所要使用的能力。)
注:除了參考光柵之外,軟件設備總是使用z來計算像素霧化效果。
當支持目相關霧化時,如果提供的投影矩陣在世界空間中產生的z值與設備空間中的w值等價,那麼系統會自動使用目相關深度來代替基於z的深度。(調用IDirect3DDevice3::SetTransform方法時,使用D3DTRANSFORMSTATE_PROJECTION值並傳遞一個D3DMATRIX結構來代表需要的矩陣,這樣來設置投影矩陣。)如果投影矩陣不滿足需要,霧化效果將會不正確。有關產生一個合適的矩陣的內容,請參看“一個W-Friendly投影矩陣”部分。(“什麼是投影變換?”部分中提供的透視投影矩陣是一個合適的投影矩陣。)
使用注意事項:要使用目相關霧化,透視修正紋理映射必須有效。將D3DRENDERSTATE_TEXTUREPERSPECTIVE渲染狀態設置爲TRUE可以使透視修正紋理映射有效。對於DirectX 6.0及以後的版本,這一設置是默認的。
Direct3D在基於w(w-based)深度運算中要使用正確設置的投影矩陣。這樣,程序就必須設置一個合適的透視矩陣才能得到基於w特性,即使它們不使用Direct3D變換管道。
Direct3D檢查投影矩陣的第4列,如果係數爲[0,0,0,1](對於一個仿射投影affine projection),那麼系統會在霧化時使用基於z的深度。在這種情況下,也要在設備空間中對線性霧化聲明開始和結束距離,範圍從最近點的0.0到最遠點的1.0。
像素霧化的所有參數都通過設備渲染狀態來進行控制。它支持所有“霧化公式”中介紹的公式。將D3DRENDERSTATE_FOGTABLEMODE設置爲D3DFOGMODE枚舉類型中相應的成員,就可以得到我們想要使用的公式。
使用線性霧化公式時,通過D3DRENDERSTATE_FOGTABLESTART和D3DRENDERSTATE_FOGTABLEEND渲染狀態來設置開始和結束距離。這些距離都使用世界空間中的單位,但是當使用軟件設備時除外,這時距離是在設備空間中。
使用指數霧化公式時,D3DRENDERSTATE_FOGTABLEDENSITY渲染狀態控制霧化密度霧化密度也是一個很重要的因子,範圍從0.0到1.0(包括它們),它對能指數中的距離值進行縮放。
D3DRENDERSTATE_FOGCOLOR渲染狀態用來控制在霧化融合時使用的顏色。
我們按下述步驟使用像素霧化:
將D3DRENDERSTATE_FOGENABLE渲染狀態設置爲TURE,使霧化融合有效。在D3DRENDERSTATE_FOGCOLOR中設置所需的霧化顏色。將D3DRENDERSTATE_FOGTABLEMODE渲染狀態設置爲D3DFOGMODE枚舉類型中相應的成員,選擇所需的霧化公式。在相應的渲染狀態中設置霧化參數。包括線性霧化的開始、結束距離和指數霧化的霧化密度。
下面我們來看一個例子:
void SetupPixelFog(DWORD dwColor, DWORD dwMode) |
注:一些霧化參數需要是浮點值,但是IDirect3DDevice3::SetRenderState方法的第二個參數只接受DWORD值。上面例子中沒有進行數據變化就將一些浮點值提供給了SetRenderState,它是將浮點變量的地址賦予DWORD指針,然後再使用它們。
下面我們討論有關頂點霧化的內容:
1.7.1 關於頂點霧化1.7.2 頂點霧化參數1.7.3 使用頂點霧化
當系統執行頂點霧化時,它在多邊形的每個頂點處進行霧化運算,然後在進行光柵處理時在多邊形面內對結果進行內插。與像素霧化不同,頂點霧化只支持線性霧化公式(D3DFOG_LINEAR)。由於頂點霧化效果要通過Direct3D的光線處理和變換引擎來計算,因此它的大部分參數都要通過IDirect3DDevice3::SetLightState方法在設備的光線狀態中來進行設置。
如果你的程序不使用Direct3D來進行變換和光線處理,那麼就必須執行你自己的霧化運算。這時,你的程序可以將計算的霧化因子放置在每個頂點的鏡面顏色的alpha成分中。你可以使用任意的公式——基於範圍的或其它的。Direct3D使用提供的霧化因子在每個多邊形面內進行內插。不使用Direct3D的變化和光線處理的程序不需要通過光線狀態來設置頂點霧化參數,但是仍然要使霧化有效,並通過相關的渲染狀態設置霧化顏色。
我們通過IDirect3DDevice3::SetLightState方法設置相應的設備光線狀態來控制頂點霧化參數。目前,頂點霧化值支持線性霧化公式,它要通過將D3DLIGHTSTATE_FOGMODE燈光狀態設置爲D3DFOG_LINEAR來進行選擇。通過D3DLIGHTSTATE_FOGSTART和D3DLIGHTSTATE_FOGEND燈光狀態來設置線性霧化的開始和結束距離。所有的距離都在世界空間中。
注:儘管目前頂點霧化不支持指數公式,但是SetLightState方法所使用的D3DLIGHTSTATETYPE枚舉類型中仍然包含了一個D3DLIGHTSTATE_FOGDENSITY成員。這個成員目前是沒有用的,它是爲將來的能力擴展而設置的。
霧化融合使用顏色通過D3DRENDERSTATE_FOGCOLOR設備渲染狀態來進行控制。
執行自己的變換和光線處理的程序也必須執行自己的頂點霧化運算,並且也不能再使用燈光狀態了。這樣一來,這樣的程序就只需要使霧化融合有效和通過相應的渲染狀態設置霧化顏色了。
按下面的步驟來使用頂點霧化:
將D3DRENDERSTATE_FOGENABLE設置爲TRUE使霧化融合有效。在D3DRENDERSTATE_FOGCOLOR渲染狀態中設置霧化顏色。將D3DLIGHTSTATE_FOGMODE燈光狀態設置爲D3DFOGMODE枚舉類型的一個成員來選擇一個霧化公式。(目前止支持D3DFOG_LINEAR。)在相應的燈光狀態中設置所需的霧化參數。不使用與渲染狀態相關的霧化參數——它們只適用於像素霧化。
我們來看一個例子:
|
注:一些霧化參數需要是浮點值,但是IDirect3DDevice3::SetRenderState和IDirect3DDevice3::SetLightState方法的第二個參數只接受DWORD值。上面例子中沒有進行數據變化就將一些浮點值提供給了這些方法,它是將浮點變量的地址賦予DWORD指針,然後再使用它們。
當我們創建一個三維場景時,我們可以通過對一些看起來具有三維效果的二維對象進行渲染的方法來提高性能。公告板技術正是出於這種思想來考慮的。
通常,公告板就是指路邊的一些告示牌。Direct3D程序中,可以通過定義一個固定的矩形並對它使用紋理的方法來創建和渲染這樣一種公告板。三維圖形學中的公告板就是這種公告板的一種擴展。它的目的就是使二維對象能夠看起來具有三維效果。這項技術將一幅包含了對象圖象的紋理應用到一個矩形圖元上。這個圖元隨着觀察者不斷旋轉,使它總能朝向觀察者。我們不用考慮對象的圖象是否是矩形。公告板上的某些地方可以是透明的,這樣就能使我們不想見到的公告板圖象的某些部分變得不可見。
許多遊戲都對它們的動畫精靈使用公告板技術。例如,當我們在一個三維迷宮中行走時,我們可能會看到一些武器或者其他一些獎品,並且能夠拾起它們。這些物品通常都是將一些二維紋理圖象應用到一個矩形圖元上生成的。我們還經常使用公告板技術來渲染場景中的樹木和灌木叢等的圖象。
當我們要將一個圖象應用到一個公告板時,我們首先得將矩形圖元進行旋轉,使圖象能夠朝向觀察者的方向。然後,程序將它平移到所需的位置。接着,就可以將紋理應用到圖元上了。
公告板技術最好能用於對稱的物體,特別是沿垂直方向對稱的物體。它還要求觀察者的水平高度不能太高,否則就有可能很明顯的感覺到對象是二維的,而不是三維的。
雲、煙和蒸汽效果都可以通過公告板技術來實現。通過沿兩個軸對公告板進行旋轉,我們就可以從任意角度看到公告板的內容。通常,我們的程序都是沿着水平和垂直兩個軸來進行旋轉。
要製造一個簡單的雲的效果,我們可以將一個矩形圖元沿一個或兩個軸進行渲染,使它總能朝向觀察者方向。然後,將一幅雲的紋理應用到圖元上,並使用透明處理。我們也可以根據時間的變化改變雲的圖象來產生動畫效果。
我們也可以使用一組圖元來產生更加複雜的雲的效果。雲的每一部分都是一個矩形圖元。這些圖元可以隨時間獨立的移動位置,從而產生出動態效果。下圖展示了這種方法:
煙的產生方法與雲相似。通常,煙需要多個公告板,就和複雜的雲一樣。一般情況下,煙需要有翻滾和上升的效果,這樣就要求組成煙的公告板能夠進行相應的移動。隨着煙的上升與擴散,需要的公告板數量也會增加。
蒸汽效果實際上就是一些不會上升的煙。但是與煙一樣,它也會逐漸擴散。下面的插圖展示了使用公告板來模擬蒸汽效果的技術:
這一部分我們介紹使用紋理融合技術來產生一些特殊效果或增加真實感等的內容。我們分以下幾個部分來進行討論:
4.1 凹凸映射(Bump Mapping)4.2 細節紋理映射(Detail Texture Mapping)
紋理融合技術可以爲一個圖元創建具有複雜紋理的表面。特別是對於光滑的表面,效果明顯。例如,我們可以將一幅經過拋光的平滑的木穀倉的紋理應用到場景中的一個桌面上。
但是,如果只使用紋理融合技術就不能滿足粗糙表面的要求了,比如一些粗糙的樹皮等。但幸運的是,Direct3D中有一種被稱爲凹凸映射(Bump Mapping)可以解決這一問題。一個凹凸映射實際上就是一幅包含了深度信息的紋理。也就是說,它儲存了一些用來表示表面上高低位置的值。
程序使用融合stage將凹凸映射應用到紋理上。將包含凹凸映射的融合stage的紋理融合操作設置爲D3DTOP_BUMPENVMAP或D3DTOP_BUMPENVMAPLUMINANCE。詳細內容見D3DTEXTUREOP和“創建融合stage”部分。
通過多紋理或多通道融合,Direct3D使程序可以細節紋理應用到圖元上。使用細節紋理,可以在表面上產生磨損的痕跡、凹凸的表面以及其他一些表面屬性。我們也可以對使用細節紋理應用mipmap。詳細內容,清參看“Mipmap紋理過濾”部分。
細節紋理還可以被用作深度提示(depth cue)。例如,我們現在要模擬一架正在着陸的直升機。當直升機接近地面時,由於放大,地面紋理會變得模糊不清。這時,我們要區分到地面的距離就會有一定困難。如果我們對地面使用了類似於沙礫等物體的細節紋理,我們就可以獲得足夠的深度提示來正確的操縱降落的直升機。
如果觀察者遠離圖元,我們大概不會想得到很多的細節展示。但要注意,當程序不使用細節紋理時,圖元可能會看起來更加明亮一些。這時,我們可以通過使用一個光線映射紋理使圖元變暗來進行補償。
我們可以使用Direct3D來模擬自然界中能量釋放的各種現象。例如,我們可以將一些火焰一樣的紋理應用到公告板上來產生火的效果。我們還可以利用一系列的紋理來產生動畫效果,並可以控制公告板變化的速度來提高真實感。如果我們要看到三維的火焰效果,可以將公告板分層放置,然後再對它們使用紋理。
閃光效果可以通過將連續的明亮光線映射到場景中所有的圖元上來進行模擬。但是這種方法的系統開銷很大,因此只能允許在某些區域內來使用。也就是說,場景中閃光出現的地方可以先使用這種方法。
還有一種技術是將公告板放置在視口的前面,這樣就擋住了整個視口。然後,我們將一些連續的白色紋理應用到公告板上,並根據時間逐漸減少透明度,這樣,整個場景就會逐漸的消退成白色。這是一種系統開銷較小的模擬閃光的方法。但是,這種方法很難模擬從一個點光源產生的閃光。
使用上面所說的模擬火焰、閃光效果的方法,我們也可以模擬爆炸效果。我們可以用一個公告板來模擬爆炸的衝擊波產生的一縷煙霧,同時,使用一系列的公告板來模擬火焰,另外還可以在視口前放置一個單獨的公告板來模擬閃光效果。
能量柱(beam)可以使用公告板來進行模擬。也可以使用用直線列表和直線帶定義的圖元來進行模擬。
程序可以使用公告板或者是由三角形列表定義的圖元來創建force fields。使用圖元時,要在三角形列表中定義一些分離的三角形,它們均勻的覆蓋住force field所佔的區域。三角形之間可以有縫隙,這樣就能看到三角形後面的場景了。然後,將紋理應用到三角形上。
如果我們要在一個三維場景體現出物體的速度感,我們可以將這個物體模糊化來進行處理,同時可以在物體後面留下一條模糊的運動痕跡。Direct3D程序可以通過在每一幀中對物體進行多次渲染的方法來實現之一效果。
我們知道,Direct3D程序通常是將場景渲染到一個離屏緩衝器中。當程序調用IDirectDrawSurface4::Flip方法時,在將緩衝器的內容顯示到屏幕上。這樣,我們就可以在顯示之前對每一幀中的對象進行多次渲染。
因此,程序需要多次調用DrawPrimitive的方法,重複的傳遞同一個三維物體。在每次調用之前,物體的位置要有輕微的變化,這樣就能製造出一系列模糊的物體圖象。如果物體有一個或多個紋理,我們可以將它所有的紋理進行透明處理,然後渲染出來作爲運動模糊的第一幅圖象。以後每次渲染這物體時,將它的透明度逐漸減小。當在最後的位置上渲染物體時,物體的紋理不能透明。但是如果我們需要紋理透明效果時,可以將最後位置的物體紋理處理爲透明。在任何情況下,幀中物體的原始圖象應該是最透明的,最後一幅圖象應該是最不透明的。
當我們渲染完一系列物體圖象以及場景中其他部分之後,就可以調用IDirectDrawSurface4::Flip方法來顯示這一幀了。
如果我們要模擬觀察者高速移動的效果,可以對整個場景進行模糊處理。這樣,程序就需要在每一幀中對整個場景進行多次渲染。每一次渲染場景時,還必須移動觀察點的位置。如果場景十分複雜,那麼當移動速度增加時,效果就會有所降低,這是因爲每一幀中需要渲染的東西增加的緣故。