一、模板緩沖
與模板緩沖相關的操作有兩種——比較操作和更新操作。
1. 比較操作
Stencil Test 比較的是Reference和Stencil Buffer中的值,公式如下:
(Stencil Ref \ &mask ) op (Stencil Buffer \ &mask ) // 左右順序不可顛倒
相應的DX和OpenGL中的命令為:
glStencil(cmp_fun, ref, mask) 和 device->SetRenderState(D3DRS_STENCILFUNC, cmp_fun)
其中的cmp_fun就是上式中的“op”,實際為 “〉”、“〈”、“〉=”、“〈=”等比較運算。所謂的Stencil Test就是進行op運算。op運算的結果是與Stencil Test的狀態相對應的(Stencil Test只有pass、fail兩種狀態),其對應關系如下:
op的運算結果 Stencil Test
true pass
false fail
當op被設為always(DX:D3DCMP_ALWAYS | OPENGL:GL_ALWAYS)運算時,無論Stencil Ref和Stencil Buffer的當前值如何,Stencil Test永遠為pass。
2. 更新操作
Stencil Test有pass/fail兩種結果,Z-Buffer Test也有pass/fail兩種結果。Stencil Buffer中的值的更新,需要同時指明是在何種狀態(實際上是由Stencil Test和Z-Buffer Test運算結果兩兩組合而成的一共四種狀態)下進行的何種更新。更新操作命令格式如下:
DX: device->SetRenderState(state, op)
OpenGL: glstencilOp(op1, op2, op3)
OpenGL DX(state值)
Stencil Test pass 參數op3 D3DRS_STENCILPASS
Stencil Test fail 參數op1 D3DRS_STENCILFAIL
Z Buffer Test fail 參數op2 D3DRS_STENCILZFAIL
OpenGL(op1/op2/op3的值) DX(op的值)
GL_KEEP D3DSTENCILOP_KEEP
GL_ZERO D3DSTENCILOP_ZERO
GL_REPLACE D3DSTENCILOP_REPLACE //用reference值替換模板緩沖中相應位置的值
GL_INCR D3DSTENCILOP_INCRSAT
GL_DECR D3DSTENCILOP_DECRSAT
GL_INCR_WRAP D3DSTENCILOP_INCR
GL_DECR_WRAP D3DSTENCILOP_DECR
要注意加以區分的兩個概念——“stencil test通過后對顏色緩沖值的更新”和“stencil test通過后對模板緩沖值的更新”。
3. Stencil Reference
Stencil Reference主要有兩個作用:
一是,在比較操作中用作進行比較的一個值;二是,在更新操作中用來替換模板緩沖中的相應數據(GL_REPLACE/D3DSTENCILOP_REPLACE)。
4. Z-Buffer Test 與 Stencil Test 的先后順序
在OpenGL中Depth Test(Z-Buffer Test)是在Stencil Test之后,參見。
而在DirectX中Z-Buffer Test 是在Stencil Test之前,參見。 // 這里是DX9的資料,DX11后渲染管線有較大變化,不知道這一順序是否有變。
二、陰影體(Shadow Volume)
通過陰影體來描繪物體投影的技術,在很多地方都有相關的介紹,但多不夠直觀。將陰影體(Shadow Volume)與Stencil Test結合來形成物體陰影的技術是其中最常見的一種方式,現結合下圖作簡單介紹。

假設,有一個對象OB在光源L作用下,在平面P上形成自身的陰影W,陰影在屏幕窗口S上對應的區域為W'(如上圖)。此時光源L與OB相交的邊沿光照方向的延長線,和OB的背光面一起將形成一個棱台VL。Shadow Volume的基本思想是這樣的:先生成一個VL的實體並將其投影到S上,但並不真的把它畫出來(也就是說,在將VL投影到S上時,並不真正地更新相應的顏色緩沖),而只是借此操作來設置S上的W'區域所對應的模板緩沖的值,同時使W'所對應的模板緩沖中的值與其它區域所對應的模板緩沖中的值區別開來。這樣就可以在接下來的過程中,借助模板緩沖將S上的W'區域塗黑,形成最終的陰影。
通過VL的投影過程來形成W'區域與其它區域的Stenclil Buffer值的差別,是基於這樣一種思想:假設有一條視線V1與VL正面(面對觀察者或攝像機)相交於點b,然后又與VL背面(背對觀察者或攝像機)相交於點c,那么說明V1所經過的區域沒有其它的物體,所以VL就不會在此處有陰影投射,也就是說V1與S的交點a上的顏色,將由場景中的其它對象來決定;假設有另一條視線V2與VL正面相交於點e,在與VL背面相交之前先與平面P相交於點f,這就說明點f必處於OB的陰影中,那么V2與S的交點d上的顏色必然要帶上陰影色。以下的技術手段是對這一原理的實現。
先將VL的正面(面向觀察者或攝像機)在S上作一次投影(不更新顏色緩沖值),同時使S上與其相應的區域(在上圖中沒有畫出)所對應的模板緩沖值加一;然后,再將VL的背面(背向觀察者或攝像機)在S上再作一次投影(不更新顏色緩沖值),同時使S上與其相應的區域(在上圖中沒有畫出)所對應的模板緩沖值減一。當然,在此過程中場景里所有其它對象也要渲染到S上。
在渲染平面P時,Z-Buffer的Test和Write功能正常開啟,在對VL的正、背面進行投影時,Z-Buffer的Test功能開啟,而Write功能關閉。這樣就使得在VL正面投影到S上時,不會更新Z-Buffer中的值,而平面P在渲染時會更新Z-Buffer的值。在V2所經過的路徑上,由於平面P處於構成VL背面的諸三角形之前,因此,在Z-Buffer Test階段,構成VL背面的諸三角形在光柵化階段將被丟棄,所以,VL背面投影操作所附帶的對相應區域的模板緩沖值的減一操作也就自動取消了,最終使得f點所對應的模板緩沖值,在加一操作(進行VL正面投影時進行)后就一直不變了;而在V1所經過的路徑上,由於沒有其它的物體的阻隔,對模板緩沖區的加一、減一操作都會按預期進行。這樣一來,W'區域與其它區域相應的模板緩沖值就被區別開來了。
(注意,DX與OpenGL雖然在Stencil Test和Z-Buffer Test的先后順序上有所不同,但兩者對Stencil Buffer值的設置都是在Z-Buffer Test和Stencil Test都完成之后才進行的。)
