模板緩存是一個(gè)離屏緩存,我們能夠用它來(lái)完成一些特效。模板緩存與后臺(tái)緩存和深度緩存有相同的定義,因此在模板緩存中的像素與后臺(tái)緩存和深度緩存中的像素是相協(xié)調(diào)的。就象名字所說(shuō),模板緩存就象一個(gè)模板它允許我們刷新渲染后緩存的某個(gè)部分。
舉例,當(dāng)要實(shí)現(xiàn)一個(gè)鏡子時(shí),我們只需要簡(jiǎn)單地反射一個(gè)物體細(xì)節(jié)到鏡子平面上;然而,我們僅僅想只繪制鏡子里的反射結(jié)果。我們能用模板緩存來(lái)渲染它,圖8.1清楚的顯示了這一點(diǎn)。

模板緩存是Direct3D中的一小部分,它是通過(guò)一個(gè)簡(jiǎn)單的表面而被約束的。就象混合,這個(gè)簡(jiǎn)單的表面提供了可變的強(qiáng)大的設(shè)置能力。有效地學(xué)習(xí)使用模板緩存最好的方法是通過(guò)學(xué)習(xí)實(shí)際的應(yīng)用程序。一旦你學(xué)懂了一點(diǎn)應(yīng)用程序中的模板緩存,你將會(huì)得到一個(gè)更好的用于你自己需要特效的主意。
為了使用模板緩存,我們?cè)诔跏蓟疍irect3D時(shí)必須首先請(qǐng)求一個(gè),然后必須啟用它。為了啟用模板緩存,我們必須設(shè)置D3DRS_STENCILENABLE渲染狀態(tài)并且指定它為true(關(guān)閉它即可指定為false)。下面的代碼是啟用和關(guān)閉模板緩存的代碼:
|
Device->SetRenderState(D3DRS_STENCILENABLE,
true);
...
// do stencil work
Device->SetRenderState(D3DRS_STENCILENABLE,
false);
|
我們可以使用IDirect3DDevice9::Clear方法來(lái)清除模板緩存并讓其擁有默認(rèn)值。回憶一下,同樣的方法被用在清除后緩存和深度緩存中。
|
Device->Clear(0, 0,
D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER | D3DCLEAR_STENCIL, 0xff000000, 1.0f,
0 );
|
注意我們已經(jīng)添加了D3DCLEAR_STENCIL到第三個(gè)參數(shù)中,它表示我們想把模板緩存和目標(biāo)(后緩存)以及深度緩存一起清除。有6種值可以用來(lái)指定清除后的模板緩存;在這個(gè)例子中我們將它清除為0。
8.1.1請(qǐng)求一個(gè)模板緩存
在我們創(chuàng)建深度緩存的同時(shí)一個(gè)模板緩存能夠被創(chuàng)建。當(dāng)指定深度緩存格式的時(shí)候,我們同時(shí)指定模板緩存的格式。這樣,模板緩存和深度緩存分享同一個(gè)離屏表面緩存,但是每個(gè)像素被指定到各自緩存內(nèi)存片段中。下面列出了3種深度/模板緩存的格式:
D3DFMT_D24S8—這種格式是說(shuō)創(chuàng)建一個(gè)32位深度/模板緩存,其中24位為深度緩存,8位為模板緩存。
D3DFMT_D24X4S4—這種格式是說(shuō)創(chuàng)建一個(gè)32位深度/模板緩存,其中24位為深度緩存,4位為模板緩存,還有4位留著不用。
D3DFMT_D15S1—這種格式是說(shuō)創(chuàng)建一個(gè)16位深度/模板緩存,其中15位為深度緩存,1位為模板緩存。
注意,還有一些格式?jīng)]有分配任何位給模板緩存。例如,D3DFMT_D32格式是說(shuō)只創(chuàng)建一個(gè)32位深度緩存。
同樣,不同硬件對(duì)模板緩存的支持也是不同的。例如有些顯卡就不支持8位模板緩存。
8.1.2模板測(cè)試
如前所述,我們能夠使用模板緩存來(lái)阻止渲染后緩存中的某些部分。阻止特殊像素被寫是通過(guò)模板測(cè)試(stencil
test)來(lái)決定的,這是通過(guò)下面的表達(dá)式來(lái)完成的:
|
(ref & mask)
ComparisonOperation (value & mask)
|
模板測(cè)試是對(duì)每個(gè)像素進(jìn)行的,假設(shè)模板是被允許。將有兩個(gè)操作:
左手邊操作數(shù)(LHS=ref&mask)
右手邊操作數(shù)(RHS=value&mask)
模板測(cè)試比較LHS和RHS,通過(guò)比較運(yùn)算來(lái)指定。全部的運(yùn)算都得到一個(gè)布爾值(true/false)。假如測(cè)試的結(jié)果是true,那么我們把像素寫入后緩存。假如測(cè)試的結(jié)果是false,我們就阻止像素被寫入后緩存。當(dāng)然,如果像素不能被寫入后緩存,那么它也不能被寫入深度緩存。
8.1.3控制模板測(cè)試
Direct3D允許我們控制變量用于模板測(cè)試。換句話說(shuō),我們可以指定參考值(stencil reference)和掩碼(mask
value),以便進(jìn)行比較運(yùn)算。雖然我們不能明確地設(shè)定模板值(stencil value),但是我們能夠控制寫入模板緩存的值。
8.1.3.1模板參考值(Reference
Value)
模板參考值ref的默認(rèn)值為0,但是我們能夠通過(guò)設(shè)置D3DRS_STENCILREF渲染狀態(tài)來(lái)改變它。例如,下面的代碼就是設(shè)置模板參考值為1:
|
Device->SetRenderState(D3DRS_STENCILREF, 0x1);
|
注意我們往往使用16進(jìn)制,因?yàn)檫@讓它看起來(lái)比整數(shù)更容易象一個(gè)位隊(duì)列,并且當(dāng)我們做位操作時(shí)這樣看起來(lái)更有用,比如相加。
8.1.3.2模板掩碼
模板掩碼值mask是被用來(lái)掩飾(隱藏)在ref和value變量中的位。它的默認(rèn)值是0xffffffff,也就是沒(méi)有掩飾任何位。我們能夠通過(guò)設(shè)置D3DRS_STENCILMASK渲染狀態(tài)來(lái)改變它。下面的例子就是掩飾高16位:
|
Device->SetRenderState(D3DRS_STENCILMASK, 0x0000ffff);
|
8.1.3.3模板值(Stencil
Value)
假如我們對(duì)第i行第j列的像素進(jìn)行模板測(cè)試,那么該值將被寫入第i行第j列的模板緩存。我們不能明確地設(shè)置個(gè)別模板值,但是可以清除模板緩存。我們能夠使用模板渲染狀態(tài)來(lái)控制將什么寫入模板緩存。
8.1.3.4比較運(yùn)算
我們能夠通過(guò)設(shè)置D3DRS_STENCILFUNC渲染狀態(tài)來(lái)設(shè)置比較運(yùn)算。這個(gè)比較運(yùn)算能夠被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——模板測(cè)試永不成功。
D3DCMP_LESS——假如LHS < RHS,那么模板測(cè)試成功。
D3DCMP_EQUAL——假如LHS = RHS,那么模板測(cè)試成功。
D3DCMP_LESSEQUAL——假如LHS <= RHS,那么模板測(cè)試成功。
D3DCMP_GREATER——假如LHS > RHS,那么模板測(cè)試成功。
D3DCMP_NOTEQUAL——假如LHS <> RHS,那么模板測(cè)試成功。
D3DCMP_GREATEREQUAL——假如LHS >= RHS,那么模板測(cè)試成功。
D3DCMP_ALWAYS——模板測(cè)試總是成功。
8.1.3更新模板緩存
除了決定是否寫或阻止一個(gè)特殊像素被寫入后緩存以外,我們能夠定義模板緩存基于三種可能的案例怎樣被更新:
對(duì)于ijth像素模板測(cè)試失敗。我們能夠定義怎樣更新在模板緩存中的ijth,通過(guò)設(shè)置D3DRS_STENCILFAIL渲染狀態(tài)來(lái)適應(yīng)這種情形:
|
Device->SetRenderState(D3DRS_STENCILFAIL, StencilOperation);
|
對(duì)于ijth像素深度測(cè)試失敗。我們能夠定義怎樣更新在模板緩存中的ijth,通過(guò)設(shè)置D3DRS_STENCILZFAIL渲染狀態(tài)來(lái)適應(yīng)這種情形:
|
Device->SetRenderState(D3DRS_STENCILZFAIL, StencilOperation);
|
對(duì)于ijth像素模板測(cè)試和深度測(cè)試都成功。我們能夠定義怎樣更新在模板緩存中的ijth,通過(guò)設(shè)置D3DRS_STENCILPASS渲染狀態(tài)來(lái)適應(yīng)這種情形:
|
Device->SetRenderState(D3DRS_STENCILPASS, StencilOperation);
|
其中StencilOperation能夠是下面預(yù)先定義的常數(shù):
D3DSTENCILOP_KEEP——指定不改變模板緩存。
D3DSTENCILOP_ZERO——指定設(shè)置模板緩存入口為0。
D3DSTENCILOP_REPLACE——指定用模板參考值(reference value)來(lái)替換模板緩存入口。
D3DSTENCILOP_INCRSAT——指定增加模板緩存入口。假如增加的值超過(guò)了允許的最大值,我們就設(shè)置它為最大值。
D3DSTENCILOP_DECRSAT——指定減少模板緩存入口。假如減少后的值小于了0,我們就設(shè)置它0。
D3DSTENCILOP_INVERT——指定按位取反模板緩存入口。
D3DSTENCILOP_INCR——指定增加模板緩存入口。假如增加的值超過(guò)了允許的最大值,我們就設(shè)置它為0。
D3DSTENCILOP_DECR——指定減少模板緩存入口。假如減少后的值小于了0,我們就設(shè)置它為允許的最大值。
8.1.4模板寫掩碼
除了已經(jīng)提及的模板渲染狀態(tài)之外,我們能夠設(shè)置一個(gè)寫掩碼(write
mask)它將屏蔽我們寫進(jìn)模板緩存的任何值的位。我們能夠通過(guò)D3DRS_STENCILWRITEMASK渲染狀態(tài)來(lái)設(shè)置寫掩碼。它的默認(rèn)值是0xffffffff。下面的例子是掩飾高16位:
|
Device->SetRenderState(D3DRS_STENCILWRITEMASK, 0x0000ffff);
|