模板缓存是一个离屏缓存,我们能够用它来完成一些特效。模板缓存与后台缓存和深度缓存有相同的定义,因此在模板缓存中的像素与后台缓存和深度缓存中的像素是相协调的。就象名字所说,模板缓存就象一个模板它允许我们刷新渲染后缓存的某个部分。
举例,当要实现一个镜子时,我们只需要简单地反射一个物体细节到镜子平面上;然而,我们仅仅想只绘制镜子里的反射结果。我们能用模板缓存来渲染它,图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); |