下面是我对模板缓存创建的理解:
1. 模板缓存是和深度缓存一起被创建的,将深度缓存的一部分作为模板缓存使用。
深度缓存和模板缓存是在Direct3D初始化时创建的,D3DPRESENT_PARAMETERS结构便包含了深度缓存和模板缓存的格式
typedef struct D3DPRESENT_PARAMETERS {
UINT BackBufferWidth;
UINT BackBufferHeight;
D3DFORMAT BackBufferFormat;
UINT BackBufferCount;
D3DMULTISAMPLE_TYPE MultiSampleType;
DWORD MultiSampleQuality;
D3DSWAPEFFECT SwapEffect;
HWND hDeviceWindow;
BOOL Windowed;
BOOL EnableAutoDepthStencil;
D3DFORMAT AutoDepthStencilFormat;
DWORD Flags;
UINT FullScreen_RefreshRateInHz;
UINT PresentationInterval;
} D3DPRESENT_PARAMETERS,*LPD3DPRESENT_PARAMETERS;
D3DFORMAT AutoDepthStencilFormat;
可以有以下取值:
D3DFMT_D24S8 创建一个32位深度/模版缓存,其中24位为深度缓存,8位为模版缓存。
D3DFMT_D24X4S4 创建一个32位深度/模版缓存,其中24位为深度缓存,4位为模版缓存,还有4位留着不用。
D3DFMT_D15S1 创建一个16位深度/模版缓存,其中15位为深度缓存,1位为模版缓存。
2. 使用模板缓存前需要启用模板缓存
下面是启用和关闭模板缓存代码
Device->SetRenderState(D3DRS_STENCILENABLE, true);
... // do stencil work
Device->SetRenderState(D3DRS_STENCILENABLE, false);
使用IDirect3DDevice9::Clear方法来清除模板缓存并让其拥有默认值。
Device->Clear(, ,
D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER | D3DCLEAR_STENCIL,
0xff000000, 1.0f, );
此时模板缓存中的模板值(Value)已被初始化为0
3. 模板缓存是用来阻止像素被写入后台缓存中的,如果像素不能被写入后台缓存,那么它也不能被写入深度缓存。
阻止像素被写入是通过模板测试来决定的,表达式为:
(ref & mask) ComparisonOperation (value & mask)
模版测试是对每个像素进行的,假设模版是被允许。将有两个操作:
- 左手边操作数(LHS=ref&mask)
- 右手边操作数(RHS=value&mask)
模版测试比较LHS和RHS,通过比较运算来指定。全部的运算都得到一个布尔值(true/false)。假如测试的结果是true,那么我们把像素写入后缓存。假如测试的结果是false,我们就阻止像素被写入后缓存。
设置渲染状态:
Device->SetRenderState(D3DRS_STENCILFUNC, D3DCMP_ALWAYS);
Device->SetRenderState(D3DRS_STENCILREF, 0x1);
Device->SetRenderState(D3DRS_STENCILMASK, 0xffffffff);
Device->SetRenderState(D3DRS_STENCILWRITEMASK,0xffffffff);
Device->SetRenderState(D3DRS_STENCILZFAIL,D3DSTENCILOP_KEEP);
Device->SetRenderState(D3DRS_STENCILFAIL, D3DSTENCILOP_KEEP);
Device->SetRenderState(D3DRS_STENCILPASS, D3DSTENCILOP_REPLACE);
设置模版比较运算为D3DCMP_ALWAYS,这就是说让所有模版测试都通过。
假如深度测试失败了,我们指定D3DSTENCILOP_KEEP,它表明不更新模版缓存入口,保存当前值。这样做的原因是假如深度测试失败了,那么就意味着像素被“模糊”了。我们不想渲染被“模糊”的反射像素。
同样假如模版测试失败了,我们也指定D3DSTENCILOP_KEEP。但是在这里这样做不是必须的,因为我们指定的是D3DCMP_ALWAYS,当然这样的测试也就永远不会失败。
假如深度测试和模版测试都通过了,我们就指定D3DSTENCILOP_REPLACE,更新模版缓存入口,设置模版参考值为0x1。
4. 更新模板缓存,因为测试总是通过,所以被渲染的部分替换为0x1,其他部分为0
更新模板缓存前,先阻止向深度缓存写入
// 禁用向模板 深度(Z)缓冲区进行写
Device->SetRenderState(D3DRS_ZWRITEENABLE, false);
Device->SetRenderState(D3DRS_ALPHABLENDENABLE, true);
将源融合因子和目标融合因子分别指定为D3DBLEND_ZERO和D3DBLEND_ONE防止对后台缓存进行更新,
//设置 源融合因子和 目标融合因子
Device->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_ZERO);
Device->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_ONE);
将镜子写入模板缓存
Device->SetStreamSource(, VB, , sizeof(Vertex));
Device->SetFVF(Vertex::FVF);
Device->SetMaterial(&MirrorMtrl);
Device->SetTexture(, MirrorTex);
D3DXMATRIX I;
D3DXMatrixIdentity(&I);
Device->SetTransform(D3DTS_WORLD, &I);
Device->DrawPrimitive(D3DPT_TRIANGLELIST, , );
在模版缓存中,符合镜子可视像素的为0x1,其他像素为0
至此,模板缓存已创建完成。
......