您的位置:首页 > 其它

【寒江雪】模板技术

2017-01-08 18:09 204 查看

模板缓存

  模板缓存是一个专门用于制作特效的离屏缓存。模板缓存的分辨率与之前讲过的后台缓存和深度缓存的分辨率完全相同,模板缓存的像素与后台缓存,深度缓存中的像素一一对应。正所谓人如其名,模板缓存,它能让我们动态地,有针对性地决定是否将某个像素写到后台缓存中。

模板测试

  在运用模板技术来进行特效的绘制时,需要精确到每个像素。我们会根据每个像素的模板缓存的值,进行一些检查,最后判断该点是否需要绘制。最终实现一些特殊效果。而这个检查的过程就是模板测试。

模板测试的使用

创建模板缓冲区

  Direct3D在创建深度缓存的同时创建了模板缓存,并且将深度缓冲区的一部分作为模板缓冲区使用。

  回忆一下创建深度缓存的时候是在Direct3DDevice初始化的时候,在结构体中填写了第十个参数EnableAutoDepthStencil和第十一个参数AutoDepthStencilFormat。

  第十一参数可以从D3DFORMAT中取值,一般我们可以以下值中选取:

D3DFMT_D16      //深度缓存用16位存储每个像素的深度值
D3DFMT_D24S8    //深度缓存用24位存储每个像素的深度值,其中8位用于模板缓存
D3DFMT_D32      //深度缓存用32位存储每个像素的深度值


清除模板缓冲区

  清除模板缓冲区涉及到IDirect3DDevice::Clear这个方法.

  这个方法在每次渲染之前都要先调用一次,这里再提一遍:

HRESULT Clear(
[in]       DWORD    Count,
[in] const D3DRECT  *pRects,
[in]       DWORD    Flags,
[in]       D3DCOLOR Color,
[in]       float    Z,
[in]       DWORD    Stencil
);


  调用Clear的时候,需要指定我们要清理的缓冲区.在第三个参数DWORD Flags中指定,该参数的值可以用 | 连接。我们可以这么填:

D3DCLEAR_TARGET|D3DCLEAR_ZBUFFER|D3DCLEAR_STENCIL


  这表示清空后台缓存,深度缓存和模板缓存

模板测试相关参数介绍

  使用模板测试实现各种各样效果的关键是正确设置与模板测试相关的各种渲染状态.

  设置渲染状态使用IDirect3DDevice::SetRenderState方法完成。它的第一个参数在庞大的枚举类型D3DRENDERSTATETYPE中取值,该枚举类型的部分构成如下:

typedef enum D3DRENDERSTATETYPE {
.......................................
D3DRS_STENCILENABLE               = 52,
D3DRS_STENCILFAIL                 = 53,
D3DRS_STENCILZFAIL                = 54,
D3DRS_STENCILPASS                 = 55,
D3DRS_STENCILFUNC                 = 56,
D3DRS_STENCILREF
f271
= 57,
D3DRS_STENCILMASK                 = 58,
D3DRS_STENCILWRITEMASK            = 59,
........................................
D3DRS_TWOSIDEDSTENCILMODE         = 185,
D3DRS_CCW_STENCILFAIL             = 186,
D3DRS_CCW_STENCILZFAIL            = 187,
D3DRS_CCW_STENCILPASS             = 188,
D3DRS_CCW_STENCILFUNC             = 189,
.........................................
} D3DRENDERSTATETYPE, *LPD3DRENDERSTATETYPE;


D3DRS_STENCILENABLE:这个渲染状态用于启用或禁用模板处理功能.

D3DRS_STENCILFALL:这个渲染状态表示模板测试失败时进行的模板操作。默认操作为D3DSTENCILCAPS_KEEP

D3DRS_STENCILZFAIL:该渲染状态表示模板测试通过,但深度测试失败时进行的模板操作。默认的操作时D3DSTENCILCAPS_KEEP

D3DRS_STENCILPASS:这个渲染状态表示模板测试通过时进行的模板操作。进行的摸默认操作时D3DSTENCILCAPS_KEEP

D3DRS_STENCILFUNC:这个渲染状态可以指定用于模板测试的比较函数。比较函数是D3DCMPFUNC枚举常量之一,该比较函数将通过模板掩码的模板参考值与模板缓冲区中当前像素模板值比较,如果为TRUE,则通过模板测试。

D3DRS_STENCILREF:这个渲染状态用于设置模板参考值,默认为0

D3DRS_STENCILMASK:这个渲染状态用于设置模板掩码,决定对模板参考值和模板缓冲区值的哪位进行比较。默认值为0xffffffff

D3DRS_STENCILWRITEMASK:这个渲染状态用于指定写入模板缓冲区中的数值的掩码,默认值为0xffffffff

D3DRS_TWOSIDEDSTENCILMODE:这个渲染状态英语激活或启用双面缓冲区

D3DRS_CCW_STENCILFAIL:这个渲染状态用于设置在启用了双面模板缓冲区后,顶点按照逆时针顺序组成的多边形当模板测试失败时进行的模板操作。

D3DRS_CCW_STENCILZFAIL:这个渲染状态用于设置在启用了双面模板缓冲区后,顶点按照逆时针顺序组成的多边形当模板测试通过但深度测试失败时进行的模板操作。

D3DRS_CCW_STENCILPASS:这个渲染状态用于设置在启用了双面模板缓存后,顶点按照逆时针顺序组成的多边形当模板测试成功时进行的模板操作。

D3DRS_CCW_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, /* force 32-bit size enum */
} D3DCMPFUNC;


枚举类型值说明
D3DCMP_NEVER深度测试函数总是返回FASLE
D3DCMP_LESS测试点深度值小于深度缓冲区中相应值时,返回TRUE,默认值
D3DCMP_EQUAL测试点深度值等于深度缓冲区中相应值时,返回TRUE
D3DCMP_LESSEQUAL测试点深度值小于等于深度缓冲区相应值时,返回TRUE
D3DCMP_GREATER测试点深度值大于深度缓冲区相应值时,返回TRUE
D3DCMP_NOTEQUAL测试点深度值不等于深度缓冲区相应值时,返回TRUE
D3DCMP_GREATEREQUAL测试点深度值大于等于深度缓冲区相应值时,返回TRUE
D3DCMP_ALWAYS测试始终为TRUE
D3DCMP_FORCE_DWORD这个枚举一般不用,保证将D3DCMPFUNC枚举类型编译为32位
  对于目标表面上的每一个像素,Direct3D首先将应用程序定义的模板参考值和模板掩码进行逐位与运算,然后将当前测试的像素在模板缓存的数值与模板掩码进行逐位与运算,最后根据模板比较函数对得到的结果进行比较,如果模板测试成功,那么该像素就被写入后台缓存。如果失败,该像素就不会被写入后台缓存,也不会被写入深度缓存.

  上面我们讲到的渲染状态D3DRS_STENCILFAIL,D3DRS_STENCILZFAIL,D3DRS_STENCILPASS定义了模板测试,深度测试失败或者通过时进行的模板操作,它们也是在一个枚举类型中取值,这个枚举类型是D3DSTENCILOP,其定义如下:

typedef enum D3D11_STENCIL_OP {
D3DSTENCILOP_KEEP      = 1,
D3DSTENCILOP_ZERO      = 2,
D3DSTENCILOP_REPLACE   = 3,
D3DSTENCILOP_INCR_SAT  = 4,
D3DSTENCILOP_DECR_SAT  = 5,
D3DSTENCILOP_INVERT    = 6,
D3DSTENCILOP_INCR      = 7,
D3DSTENCILOP_DECR      = 8,
D3DSTENCILOP_FORCE_DWROD=0x7fffffff
} D3DSTENCILOP,*LPD3DSTENCILOP;


枚举类型值(模板操作)精析
D3DSTENCILOP_KEEP是默认的选项。表示不更新模板缓冲区中的值
D3DSTENCILOP_ZERO将模板缓存中的值置零
D3DSTENCILOP_REPLACE用模板参考值替换模板缓冲区中对应的值
D3DSTENCILOP_INCR_SAT增加模板缓冲区中的对应数值,如果大于最大值则取最大值
D3DSTENCILOP_DECR_SAT减小模板缓冲区中的对应数值,如果小于最小值则取最小值
D3DSTENCILOP_INVERT倒置模板缓冲区中对应的数据位
D3DSTENCILOP_INCR增加模板缓冲区中对应的数值,如果大于最大值则取0
D3DSTENCILOP_DECR减小模板缓冲区中对应的数值,如果小于0则等于最大值
D3DSTENCILOP_FORCE_DWROD这个枚举值一般不用,用于保证将D3DCMPFUNC枚举类型编译为32位

对模板测试的理解

  模板测试使用模板参考值,模板掩码,模板比较函数和当前像素在模板缓冲区中的模板值作为参数,判断某个像素是否被写入到后台缓冲区中。模板测试的表达式如下:

result=(ref & maskref)OP(value & maskwrite)

  其中的ref表示模板参考值,mask表示模板掩码,value表示模板缓冲中的值,OP表示模板比较函数.

  在Direct3D进行模板测试之前,我们需要对模板测试的模板参考值,模板掩码和模板比较函数做一下设置。模板参考值的默认值是0,需要我们另外设置。

镜面效果示例代码

//绘制镜面
g_pd3dDevice->SetTransform(D3DTS_WORLD, &g_BoxWorld);
g_pd3dDevice->SetMaterial(&g_pBoxMat);
g_pBoxMesh->DrawSubset(0);

g_pd3dDevice->SetRenderState(D3DRS_STENCILENABLE, true);//开启模板缓存
g_pd3dDevice->SetRenderState(D3DRS_STENCILREF, 0x1);    //设置模板参考值
g_pd3dDevice->SetRenderState(D3DRS_STENCILMASK, 0xffffffff);//设置模板掩码
g_pd3dDevice->SetRenderState(D3DRS_STENCILWRITEMASK, 0xffffffff);//设置模板写掩码
g_pd3dDevice->SetRenderState(D3DRS_STENCILFUNC, D3DCMP_ALWAYS);//设置比较函数,这里取总是通过
g_pd3dDevice->SetRenderState(D3DRS_STENCILPASS, D3DSTENCILOP_REPLACE);//设置通过后的操作,这里设置为将参考值写入模板缓存

g_pd3dDevice->SetRenderState(D3DRS_ZENABLE, false);//禁用深度缓存
g_pd3dDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, true);//开启混合模式
g_pd3dDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_ZERO);
g_pd3dDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_ONE);

//绘制镜面 这里只写模板缓存
g_pd3dDevice->SetTransform(D3DTS_WORLD, &g_BoxWorld);
g_pd3dDevice->SetMaterial(&g_pBoxMat);
g_pBoxMesh->DrawSubset(0);

g_pd3dDevice->SetRenderState(D3DRS_ZENABLE, true);//开启深度缓存
g_pd3dDevice->SetRenderState(D3DRS_STENCILFUNC, D3DCMP_EQUAL);//设置比较操作
g_pd3dDevice->SetRenderState(D3DRS_STENCILPASS, D3DSTENCILOP_KEEP);//测试通过,不更新模板缓存
g_pd3dDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_DESTCOLOR);//使用目标颜色
g_pd3dDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_ZERO);
g_pd3dDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_CW);

D3DXMATRIX matReflect;
D3DXPLANE planeXY(0.0f, 0.0f, 1.0f, 0.0f);
D3DXMatrixReflect(&matReflect, &planeXY);
matHero = matHero*g_matWorld;       //计算镜像相对于本体的位置
matWorld = matReflect*matHero;

//绘制镜像
g_pd3dDevice->SetTransform(D3DTS_WORLD, &matWorld);
for (DWORD i = 0; i < g_dwNumMtrls; i++) {
g_pd3dDevice->SetMaterial(&g_pMaterials[i]);
g_pd3dDevice->SetTexture(0, g_pTextures[i]);
g_pMesh->DrawSubset(i);
}

//恢复渲染状态
g_pd3dDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, false);
g_pd3dDevice->SetRenderState(D3DRS_STENCILENABLE, false);
g_pd3dDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_CCW);


Copyright© by 寒江雪

Date:2017.1.8
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: