模版缓存是一个远离屏幕的缓存,我们能够用它来完成一些特效。模版缓存与后缓存和深度缓存有相同的定义,因此在模版缓存中的[i][j]像素与后缓存和深度缓存中的[i][j]像素是相协调的。就象名字所说,模版缓存就象一个模版它允许我们印刷渲染后缓存的某个部分。
1.使用模板缓存
1.1启用模版缓存
Device->SetRenderState(D3DRS_STENCILENABLE,true);
我们可以使用IDirect3DDevice9::Clear方法来清除模版缓存并让其拥有默认值,也用在1.2清除后缓存和深度缓存中
Device->Clear(0,0,D3DCLEAR_TARGET|D3DCLEAR_ZBUFFER|D3DCLEAR_STENCIL,0xffffffff,1.0f,0);
表示把模版缓存和目标(后缓存)以及深度缓存一起清除。
2.创建模板缓存
在我们创建深度缓存的同时一个模版缓存能够被创建。当指定深度缓存格式的时候,我们同时指定模版缓存的格式。这样,模版缓存和深度缓存分享同一个离屏表面缓存,但是每个像素被指定到各自缓存内存片段中。
下面列出了3种深度/模版缓存的格式:
D3DFMT_D24S8—一个32位深度/模版缓存,其中24位为深度缓存,8位为模版缓存。
D3DFMT_D24X4S4—一个32位深度/模版缓存,其中24位为深度缓存,位为模版缓存,还有4位留着不用。
D3DFMT_D15S1—一个16位深度/模版缓存,其中15位为深度缓存,1位为模版缓存。
3.模板测试(stencil test)
能够使用模版缓存来阻止渲染后缓存中的某些部分,阻止特殊像素被写是通过模版测试(stencil test)来决定的,这是通过下面的表达式来完成的:
(ref & mask) ComparisonOperation (value & mask)
模版测试是对每个像素进行的,假设模版是被允许。将有两个操作:
左手边操作数(LHS=ref&mask) 右手边操作数(RHS=value&mask)
模版测试比较LHS和RHS,通过比较运算来指定。全部的运算都得到一个布尔值(true/false)。假如结果是true,那么我们把像素写入后缓存。反之,就阻止像素被写入后缓存。如果像素不能被写入后缓存,那么它也不能被写入深度缓存。
控制模版测试
指定参考值(stencil reference)和掩码(mask value),以便进行比较运算。虽然不能明确地设定模版值(stencil value),但是能够控制写入模版缓存的值。
模版参考值(Reference Value)
模版参考值ref的默认值为0,通过设置D3DRS_STENCILREF渲染状态来改变:
Device->SetRenderState(D3DRS_STENCILREF,0x1);
值常用16位表示
模版掩码(stencil mask)
模版掩码值mask是被用来掩饰(隐藏)在ref和value变量中的位。它的默认值是0xffffffff,也就是没有掩饰任何位,通过设置D3DRS_STENCILMASK渲染状态来改变,例掩饰高16位:
Device->SetRenderState(D3DRS_STENCILMASK,0x0000ffff);
模版值(Stencil Value)
在模版缓存中我们进行模版测试的当前像素。对像素进行模版测试,那么该值将被写入该模版缓存。不能明确地设置个别模版值,但是可以清除模版缓存。能够使用模版渲染状态来控制将什么写入模版缓存。
比较运算
通过设置D3DRS_STENCILFUNC渲染状态来设置比较运算。这个比较运算能够被
D3DCMPFUNC的任何成员类型列举:
typedef enum D3DCMPFUNC {
D3DCMP_NEVER = 1,//Always fail the test.
D3DCMP_LESS = 2,//假如LHS < RHS,那么模版测试成功
D3DCMP_EQUAL = 3,//假如LHS = RHS,那么模版测试成功
D3DCMP_LESSEQUAL = 4,//假如LHS <= RHS,那么模版测试成功
D3DCMP_GREATER = 5,//假如LHS > RHS,那么模版测试成功
D3DCMP_NOTEQUAL = 6,//假如LHS <> RHS,那么模版测试成功
D3DCMP_GREATEREQUAL = 7,//假如LHS >= RHS,那么模版测试成功
D3DCMP_ALWAYS = 8,//always pass the test
D3DCMP_FORCE_DWORD = 0x7fffffff
} D3DCMPFUNC, *LPD3DCMPFUNC;
更新模板缓存
除了决定是否写或阻止一个特殊像素被写入后缓存以外,我们能够定义模版缓存基于三种可能的案例怎样被更新:
对于[i][j]处像素模版测试失败:
Device->SetRenderState(D3DRS_STENCILFAIL, StencilOperation);
对于[i][j]处像素深度测试失败:
Device->SetRenderState(D3DRS_STENCILZFAIL, StencilOperation);
对于[i][j]处像素模版测试和深度测试都成功:
Device->SetRenderState(D3DRS_STENCILPASS, StencilOperation);
StencilOperation是以下常数
typedef enum D3DSTENCILOP {
D3DSTENCILOP_KEEP = 1,//不改变模版缓存
D3DSTENCILOP_ZERO = 2,//设置模版缓存入口为。
D3DSTENCILOP_REPLACE = 3,//指定用模版参考值来替换模版缓存入口
D3DSTENCILOP_INCRSAT = 4,//指定增加模版缓存入口。假如增加的值超过了允许的最大值,我们就设置它为最大值。
D3DSTENCILOP_DECRSAT = 5,//指定减少模版缓存入口。假如减少后的值小于了,我们就设置它。
D3DSTENCILOP_INVERT = 6,//指定按位取反模版缓存入口。
D3DSTENCILOP_INCR = 7,//指定增加模版缓存入口。假如增加的值超过了允许的最大值,我们就设置它为0。
D3DSTENCILOP_DECR = 8,//指定减少模版缓存入口。假如减少后的值小于0,我们就设置它为允许的最大值。
D3DSTENCILOP_FORCE_DWORD = 0x7fffffff
} D3DSTENCILOP, *LPD3DSTENCILOP;
模版写掩码
除了已经提及的模版渲染状态之外,我们能够设置一个写掩码(write mask)它将掩饰我们写进模版缓存的任何值的位。我们能够通过D3DRS_STENCILWRITEMASK渲染状态来设置写掩码。它的默认值是0xffffffff。下面的例子是掩饰高16位:
Device->SetRenderState(D3DRS_STENCILWRITEMASK, 0x0000ffff);
反射矩阵:
D3DXMATRIX * D3DXMatrixReflect(
__inout D3DXMATRIX *pOut,//输出反射矩阵
__in const D3DXPLANE *pPlane//反射平面
);
3种特殊的反射变换:
关于三个坐标平面的反射—yz平面,xz平面,和xy平面—分别通过下面三个矩阵来表现:
阴影矩阵
影子本质上是把物体按照灯光照射方向平行地投射到平面n*p+d=0
之上。同样的,图8.7中所示的点光源,影子本质上是把物体按照透视画法从光源投射到平面n*p+d=0之上。
我们能够使用一个矩阵来表示从一个顶点p变换到平面n*p=d=0上的s的变化。而且,我们能够用同一个矩阵来表现正交投影和透视投影。
我们用一个4D向量(nx, ny, nz, d)来表示将要用于投射阴影平面的平面等式中的各个系数。让4D向量L=(Lx, Ly, Lz, Lw)来表示平行光的照射方向或点光源的位置。我们用w来区别:
1.假如w=0,那么L表示平行光的照射方向。
2.假如w=1 ,那么L表示点光源的位置
让k=(nx, ny, nz, d) * (Lx, Ly, Lz, Lw) = nxLx+nyLy+nzLz+dLw
那么我们就可得到表示点p到点s的变换矩阵,即阴影矩阵:
在D3DX库中已经给我们提供了一个建立阴影矩阵的函数。其中当w=0时表示平行光,当
w=1时表示点光源:
D3DXMATRIX * D3DXMatrixShadow(
__inout D3DXMATRIX *pOut,
__in const D3DXVECTOR4 *pLight,
__in const D3DXPLANE *pPlane
);
防止双倍渲染
几何学上,当我们将一个物体投影到一个平面上时,很可能会有两个或者更多的投影三角形被重叠到一起。若我们就这样渲染,那么有重叠三角形的地方就会被多次混合以至这些地方将会变得更黑。 我们设置模版测试为允许像素第一次被渲染。即,当把影子像素渲染到后缓存时,我们同时在模版缓存中做好标记。然后,如果试图把像素向一个已经渲染过的地方写,那么模版测试将会失败。
我们能够通过下面的混合等式来混合被反射的茶壶和镜子:
例子: