D3D中的模板緩存(1)
模板緩存是一個離屏緩存,我們能夠用它來完成一些特效。模板緩存與后臺緩存和深度緩存有相同的定義,因此在模板緩存中的像素與后臺緩存和深度緩存中的像素是相協調的。就象名字所說,模板緩存就象一個模板它允許我們刷新渲染后緩存的某個部分。
舉例,當要實現一個鏡子時,我們只需要簡單地反射一個物體細節到鏡子平面上;然而,我們僅僅想只繪制鏡子里的反射結果。我們能用模板緩存來渲染它,圖8.1清楚的顯示了這一點。
模板緩存是Direct3D中的一小部分,它是通過一個簡單的表面而被約束的。就象混合,這個簡單的表面提供了可變的強大的設置能力。有效地學習使用模板緩存最好的方法是通過學習實際的應用程序。一旦你學懂了一點應用程序中的模板緩存,你將會得到一個更好的用于你自己需要特效的主意。
8.1使用模板緩存
為了使用模板緩存,我們在初始化Direct3D時必須首先請求一個,然后必須啟用它。為了啟用模板緩存,我們必須設置D3DRS_STENCILENABLE渲染狀態并且指定它為true(關閉它即可指定為false)。下面的代碼是啟用和關閉模板緩存的代碼:
Device->SetRenderState(D3DRS_STENCILENABLE, true); ... // do stencil work Device->SetRenderState(D3DRS_STENCILENABLE, false); |
我們可以使用IDirect3DDevice9::Clear方法來清除模板緩存并讓其擁有默認值?;貞浺幌?,同樣的方法被用在清除后緩存和深度緩存中。
Device->Clear(0, 0, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER | D3DCLEAR_STENCIL, 0xff000000, 1.0f, 0 ); |
注意我們已經添加了D3DCLEAR_STENCIL到第三個參數中,它表示我們想把模板緩存和目標(后緩存)以及深度緩存一起清除。有6種值可以用來指定清除后的模板緩存;在這個例子中我們將它清除為0。
8.1.1請求一個模板緩存
在我們創建深度緩存的同時一個模板緩存能夠被創建。當指定深度緩存格式的時候,我們同時指定模板緩存的格式。這樣,模板緩存和深度緩存分享同一個離屏表面緩存,但是每個像素被指定到各自緩存內存片段中。下面列出了3種深度/模板緩存的格式:
D3DFMT_D24S8—這種格式是說創建一個32位深度/模板緩存,其中24位為深度緩存,8位為模板緩存。
D3DFMT_D24X4S4—這種格式是說創建一個32位深度/模板緩存,其中24位為深度緩存,4位為模板緩存,還有4位留著不用。
D3DFMT_D15S1—這種格式是說創建一個16位深度/模板緩存,其中15位為深度緩存,1位為模板緩存。
注意,還有一些格式沒有分配任何位給模板緩存。例如,D3DFMT_D32格式是說只創建一個32位深度緩存。
同樣,不同硬件對模板緩存的支持也是不同的。例如有些顯卡就不支持8位模板緩存。
8.1.2模板測試
如前所述,我們能夠使用模板緩存來阻止渲染后緩存中的某些部分。阻止特殊像素被寫是通過模板測試(stencil test)來決定的,這是通過下面的表達式來完成的:
(ref & mask) ComparisonOperation (value & mask) |
模板測試是對每個像素進行的,假設模板是被允許。將有兩個操作:
左手邊操作數(LHS=ref&mask)
右手邊操作數(RHS=value&mask)
模板測試比較LHS和RHS,通過比較運算來指定。全部的運算都得到一個布爾值(true/false)。假如測試的結果是true,那么我們把像素寫入后緩存。假如測試的結果是false,我們就阻止像素被寫入后緩存。當然,如果像素不能被寫入后緩存,那么它也不能被寫入深度緩存。
8.1.3控制模板測試
Direct3D允許我們控制變量用于模板測試。換句話說,我們可以指定參考值(stencil reference)和掩碼(mask value),以便進行比較運算。雖然我們不能明確地設定模板值(stencil value),但是我們能夠控制寫入模板緩存的值。
8.1.3.1模板參考值(Reference Value)
模板參考值ref的默認值為0,但是我們能夠通過設置D3DRS_STENCILREF渲染狀態來改變它。例如,下面的代碼就是設置模板參考值為1:
Device->SetRenderState(D3DRS_STENCILREF, 0x1); |
注意我們往往使用16進制,因為這讓它看起來比整數更容易象一個位隊列,并且當我們做位操作時這樣看起來更有用,比如相加。
8.1.3.2模板掩碼
模板掩碼值mask是被用來掩飾(隱藏)在ref和value變量中的位。它的默認值是0xffffffff,也就是沒有掩飾任何位。我們能夠通過設置D3DRS_STENCILMASK渲染狀態來改變它。下面的例子就是掩飾高16位:
Device->SetRenderState(D3DRS_STENCILMASK, 0x0000ffff); |
8.1.3.3模板值(Stencil Value)
假如我們對第i行第j列的像素進行模板測試,那么該值將被寫入第i行第j列的模板緩存。我們不能明確地設置個別模板值,但是可以清除模板緩存。我們能夠使用模板渲染狀態來控制將什么寫入模板緩存。
8.1.3.4比較運算
我們能夠通過設置D3DRS_STENCILFUNC渲染狀態來設置比較運算。這個比較運算能夠被D3DCMPFUNC的任何成員類型列舉:
typedef enum _D3DCMPFUNC { D3DCMP_NEVER = 1, D3DCMP_LESS = 2, D3DCMP_EQUAL = 3, D3DCMP_LESSEQUAL = 4, D3DCMP_GREATER = 5, D3DCMP_NOTEQUAL = 6, D3DCMP_GREATEREQUAL = 7, D3DCMP_ALWAYS = 8, D3DCMP_FORCE_DWORD = 0x7fffffff } D3DCMPFUNC; |
D3DCMP_NEVER——模板測試永不成功。
D3DCMP_LESS——假如LHS < RHS,那么模板測試成功。
D3DCMP_EQUAL——假如LHS = RHS,那么模板測試成功。
D3DCMP_LESSEQUAL——假如LHS <= RHS,那么模板測試成功。
D3DCMP_GREATER——假如LHS > RHS,那么模板測試成功。
D3DCMP_NOTEQUAL——假如LHS <> RHS,那么模板測試成功。
D3DCMP_GREATEREQUAL——假如LHS >= RHS,那么模板測試成功。
D3DCMP_ALWAYS——模板測試總是成功。
8.1.3更新模板緩存
除了決定是否寫或阻止一個特殊像素被寫入后緩存以外,我們能夠定義模板緩存基于三種可能的案例怎樣被更新:
對于ijth像素模板測試失敗。我們能夠定義怎樣更新在模板緩存中的ijth,通過設置D3DRS_STENCILFAIL渲染狀態來適應這種情形:
Device->SetRenderState(D3DRS_STENCILFAIL, StencilOperation); |
對于ijth像素深度測試失敗。我們能夠定義怎樣更新在模板緩存中的ijth,通過設置D3DRS_STENCILZFAIL渲染狀態來適應這種情形:
Device->SetRenderState(D3DRS_STENCILZFAIL, StencilOperation); |
對于ijth像素模板測試和深度測試都成功。我們能夠定義怎樣更新在模板緩存中的ijth,通過設置D3DRS_STENCILPASS渲染狀態來適應這種情形:
Device->SetRenderState(D3DRS_STENCILPASS, StencilOperation); |
其中StencilOperation能夠是下面預先定義的常數:
D3DSTENCILOP_KEEP——指定不改變模板緩存。
D3DSTENCILOP_ZERO——指定設置模板緩存入口為0。
D3DSTENCILOP_REPLACE——指定用模板參考值(reference value)來替換模板緩存入口。
D3DSTENCILOP_INCRSAT——指定增加模板緩存入口。假如增加的值超過了允許的最大值,我們就設置它為最大值。
D3DSTENCILOP_DECRSAT——指定減少模板緩存入口。假如減少后的值小于了0,我們就設置它0。
D3DSTENCILOP_INVERT——指定按位取反模板緩存入口。
D3DSTENCILOP_INCR——指定增加模板緩存入口。假如增加的值超過了允許的最大值,我們就設置它為0。
D3DSTENCILOP_DECR——指定減少模板緩存入口。假如減少后的值小于了0,我們就設置它為允許的最大值。
8.1.4模板寫掩碼
除了已經提及的模板渲染狀態之外,我們能夠設置一個寫掩碼(write mask)它將屏蔽我們寫進模板緩存的任何值的位。我們能夠通過D3DRS_STENCILWRITEMASK渲染狀態來設置寫掩碼。它的默認值是0xffffffff。下面的例子是掩飾高16位:
Device->SetRenderState(D3DRS_STENCILWRITEMASK, 0x0000ffff); |