chap 8 模版(stencil)缓存
功能:阻止后台缓存中某些特定区域的绘制
1.模版缓存的使用
启用和禁用模版缓存:
清空模版缓存,此方法也能清空深度缓存和后台缓存:
模版缓存的格式:
D3DFMT_D24S8 D3DFMT_D24X4S4 D3DFMT_D15S1
就是判定是否将某个像素写入后台缓存的决策过程(测试结果为 true 就把此像素写入后台):
(ref(模版参考值) & mask(模版掩码))ComparisonOperation(value(当前模版缓存的数值) & mask(模版掩码))
ComparisonOperation前面的是左值,后面 右值
(1).模版参考值 ref: 默认是0,可以修改:
(2).模版掩码 mask: 屏蔽ref或者value中的某些位,默认是 0xffffffff, 表示不屏蔽任何位
可以修改:
(3).模版值 value: 不能显示的单独设置模版值,但可以对模版进行清空操作,还可以通过 模版的绘制状态 控制写入模版缓存的内容
(4).比较运算: 通过绘制状态 D3DRS_STENCILFUNC 来设置比较运算,常用的值有:
模版缓存的更新
(1).某像素的模版测试失败:
(2).某像素的深度测试失败:
(3).某像素的深度测试和模版测试均成功:
模版写掩码
屏蔽将写入模版缓存的某位
2.镜面效果
D3DX提供了如下方法去创建 相对于任意平面的 镜像变换 矩阵:
镜面效果实现(理论篇):
(1)绘制场景,包括地板、墙壁、镜面和茶壶,此步 不需修改 模版缓存
(2)模版缓存 清零
(3)将构成镜面的图元 仅 绘制到 模版缓存 中,并且让模版测试总是成功,成功通过的模版缓存值被替换为1
//解析:这时,由于只画了镜面,所以模版缓存中镜面对应的区域像素设为1,其余像素都为0(这就相当于对镜面做了标记)
(4)绘制茶壶的 映像 到 后台缓存 和 模版缓存。(其实是通过先 绘制到模版缓存, 进行判断后, 再写入 后台缓存)
//解析:通过了模版测试,就将茶壶映像绘制到后台缓存中
领悟吧!奥义!
后台还缓存 是 结果, 模版缓存 是手段,
开启了模版缓存,意味着要想在后台缓存中绘制,就得先通过“模版的试练”(模版测试)
把要绘制的内容分别放进 后台缓存 和 模版缓存,在模版缓存中去测试,如果测试结果正确,就在后台缓存中绘制!
镜面效果实现(实战篇):
3.阴影效果
Direct3D中的阴影矩阵:
使用 模版缓存 防止二次融合
二次融合:就是一个物体的阴影中,同一个位置出现阴影加强的情况
阴影效果实现:
功能:阻止后台缓存中某些特定区域的绘制
1.模版缓存的使用
启用和禁用模版缓存:
Device->SetRenderState(D3DRS_STENCILENABLE, true);
Device->SetRenderState(D3DRS_STENCILENABLE, false);
清空模版缓存,此方法也能清空深度缓存和后台缓存:
Device->Clear(0,0,D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER | D3DCLEAR_STENCIL, 0xff000000, 1.0f, 0); //最后一个参数说明模板缓存要清空为0
模版缓存的格式:
D3DFMT_D24S8 D3DFMT_D24X4S4 D3DFMT_D15S1
就是在创建设备的时候,D3DPRESENT_PARAMETERS 那个结构里面
就是判定是否将某个像素写入后台缓存的决策过程(测试结果为 true 就把此像素写入后台):
(ref(模版参考值) & mask(模版掩码))ComparisonOperation(value(当前模版缓存的数值) & mask(模版掩码))
ComparisonOperation前面的是左值,后面 右值
(1).模版参考值 ref: 默认是0,可以修改:
Device->SetRenderState(D3DRS_STENCILREF, 0x1);
(2).模版掩码 mask: 屏蔽ref或者value中的某些位,默认是 0xffffffff, 表示不屏蔽任何位
可以修改:
Device->SetRenderState(D3DRS_STENCILMASK, 0x0000ffff); //表示屏蔽了高16位
(3).模版值 value: 不能显示的单独设置模版值,但可以对模版进行清空操作,还可以通过 模版的绘制状态 控制写入模版缓存的内容
(4).比较运算: 通过绘制状态 D3DRS_STENCILFUNC 来设置比较运算,常用的值有:
D3DCMP_NEVER //模版测试总是失败,比较函数总是返回false
D3DCMP_LESS //若左值 < 右值,模版测试成功
D3DCMP_EQUAL //若左值 == 右值,模版测试成功
D3DCMP_LESSEQUAL //若左值 <= 右值,模版测试成功
D3DCMP_GREATER //若左值 > 右值,模版测试成功
D3DCMP_NOTEQUAL //若左值 != 右值,模版测试成功
D3DCMP_GREATEREQUAL //若左值 >= 右值,模版测试成功
D3DCMP_ALWAYS //模版测试总是成功,比较函数总是返回true
模版缓存的更新
(1).某像素的模版测试失败:
Device->SetRenderState(D3DRS_STENCILFAIL, StencilOperation);
(2).某像素的深度测试失败:
Device->SetRenderState(D3DRS_STENCILZFAIL, StencilOperation);
(3).某像素的深度测试和模版测试均成功:
Device->SetRenderState(D3DRS_STENCILPASS, StencilOperation);
//StencilOperation的取值:
D3DSTENCILOP_KEEP //不更新模版缓存中的值
D3DSTENCILOP_ZERO //将模版缓存中的值设为0
D3DSTENCILOP_REPLACE //用模版参考值代替模版缓存中的值
//....
模版写掩码
屏蔽将写入模版缓存的某位
2.镜面效果
D3DX提供了如下方法去创建 相对于任意平面的 镜像变换 矩阵:
D3DXMATRIX* D3DXMatrixReflect(D3DXMATRIX* pout, CONST D3DXPLANE* pplane);
镜面效果实现(理论篇):
(1)绘制场景,包括地板、墙壁、镜面和茶壶,此步 不需修改 模版缓存
(2)模版缓存 清零
(3)将构成镜面的图元 仅 绘制到 模版缓存 中,并且让模版测试总是成功,成功通过的模版缓存值被替换为1
//解析:这时,由于只画了镜面,所以模版缓存中镜面对应的区域像素设为1,其余像素都为0(这就相当于对镜面做了标记)
(4)绘制茶壶的 映像 到 后台缓存 和 模版缓存。(其实是通过先 绘制到模版缓存, 进行判断后, 再写入 后台缓存)
//解析:通过了模版测试,就将茶壶映像绘制到后台缓存中
领悟吧!奥义!
后台还缓存 是 结果, 模版缓存 是手段,
开启了模版缓存,意味着要想在后台缓存中绘制,就得先通过“模版的试练”(模版测试)
把要绘制的内容分别放进 后台缓存 和 模版缓存,在模版缓存中去测试,如果测试结果正确,就在后台缓存中绘制!
镜面效果实现(实战篇):
//(1)
void RenderMirror() //注意,这个函数只是绘制了镜面的部分
{
Device->SetRenderState(D3DRS_STENCILENABLE, true);
Device->SetRenderState(D3DRS_STENCILFUNC, D3DCMP_ALWAYS);
Device->SetRenderState(D3DRS_STENCILREF, 0x1);
Device->SetRenderState(D3DRS_STENCILMASK, 0xffffffff);
Device->SetRenderState(D3DRS_STENCILWRITEMASK, 0xffffffff);
Device->SetRenderState(D3DRS_ZFAIL, D3DSTENCILOP_KEEP);
Device->SetRenderState(D3DRS_FAIL, D3DSTENCILOP_KEEP);
Device->SetRenderState(D3DRS_PASS, D3DSTENCILOP_REPLACE);
/* D3DCMP_ALWAYS说明了模版测试总是成功,D3DSTENCILOP_REPLACE则说明模版测试成功后,用模版参考值D3DRS_STENCILREF代替模版缓存中的值 */
//(2)
Device->SetRenderState(D3DRS_ZWRITEENABLE, false); //关闭深度缓存
Device->SetRenderState(D3DRS_ALPHABLENDENABLE, true);
Device->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_ZERO);
Device->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_ONE); //禁止更新后台缓存
//说明:这里只向模板缓存中绘制茶壶,开启alpha是为了不绘制到后台缓存中
Device->SetStreamSource(0, VB, 0, sizeof(Vertex));
Device->SetFVF(Vertex::FVF);
Device->SetMaterial(&MirrorMtrl);
Device->SetTexture(0, MirrorTex);
D3DXMatrixIdentity(&I);
Device->SetTransform(D3DTS_WORLD,&I);
Device->DrawPrimitive(D3DPT_TRIANGLELIST, 18, 2);
Device->SetRenderState(D3DRS_ZWRITEENABLE, true); //重新打开深度缓存
//(3)
Device->SetRenderState(D3DRS_STENCILFUNC, D3DCMP_EQUAL);
Device->SetRenderState(D3DRS_STENCILPASS, D3DSTENCILOP_KEEP); //测试成功保留模版缓存中的值
//(4)
D3DXMATRIX W,T,R;
D3DXPLANE plane(0.0f, 0.0f, 1.0f, 0.0f);
D3DXMatrixReflect(&R, &plane); //获得镜面矩阵
D3DXMatrixTranslation(
&T, //获得茶壶位置矩阵
TeapotPosition.x,
TeapotPosition.y,
TeapotPosition.z,
);
W = T * R;
//(5)
Device->Clear(0,0,D3DCLEAR_ZBUFFER,0,1.0f,0); //由于映像的位置在镜面之后,所以先清空深度缓存
Device->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_DESTCOLOR);
Device->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_ZERO); //把映像和镜面融合
Device->SetTransform(D3DTS_WORLD, &W);
Device->SetMaterial(&TeapotMtrl);
Device->SetTexture(0,0);
Device->SetRenderState(D3DRS_CULLMODE, D3DCULL_CW); //镜面中的正反和现实相反,所以背面消隐的方式要改变
Teapot->DrawSubset(0); //由于前面的设置,这里绘制的已经不是原来的茶壶了,而是茶壶的镜像
Device->SetRenderState(D3DRS_ALPHABLENDENABLE, false);
Device->SetRenderState(D3DRS_STENCILENABLE, false);
Device->SetRenderState(D3DRS_CULLMODE, D3DCULL_CCW); //改回原先的背面消隐
}
3.阴影效果
Direct3D中的阴影矩阵:
D3DXMATRIX * D3DXMatrixShadow(
D3DXMATRIX * pout,
const D3DXVECTOR4 * pLight,
const D3DXPLANE * pPlane
);
使用 模版缓存 防止二次融合
二次融合:就是一个物体的阴影中,同一个位置出现阴影加强的情况
阴影效果实现:
void RenderShadow()
{
Device->SetRenderState(D3DRS_STENCILENABLE, true);
Device->SetRenderState(D3DRS_STENCILFUNC, D3DCMP_EQUAL);
Device->SetRenderState(D3DRS_STENCILREF, 0x0);
Device->SetRenderState(D3DRS_STENCILMASK, 0xffffffff);
Device->SetRenderState(D3DRS_STENCILWRITEMASK, 0xffffffff);
Device->SetRenderState(D3DRS_STENCILZFAIL, D3DSTENCILOP_KEEP);
Device->SetRenderState(D3DRS_STENCILFAIL, D3DSTENCOLOP_KEEP);
Device->SetRenderState(D3DRS_STENCILPASS, D3DSTENCOLOP_INCR); //注意这里设为增加模板缓存中的对应值是为了防止二次融合
D3DXVECTOR4 lightDirection(0.707f, -0.707f, 0.707f, 0.0f);
D3DXPLANE groundPlane(0.0f, -1.0f, 0.0f, 0.0f);
D3DXMATRIX S;
D3DXMatrixShadow(&S, &lightDirection, &groundPlane);
D3DXMATRIX T;
D3DXMatrixTranslation(&T, TeapotPosition.X, TeapotPosition.Y, TeapotPosition.Z);
D3DXMATRIX W = T * S;
Device->SetTransform(D3DTS_WORLD, &W);
Device->SetRenderState(D3DRS_ALPHABLENDENABLE, true); //让阴影和地面融合
Device->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
Device->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
D3DMATERIAL9 mtrl = d3d::InitMtrl(d3d::BLACK, d3d::BLACK, d3d::BLACK, d3d::BLACK, 0.0f);
mtrl.Diffuse.a = 0.5f;
Device->SetRenderState(D3DRS_ZENABLE, false); //先绘制地面,然后禁用深度缓存,最后绘制阴影,这是为了防止深度冲突
Device->SetMaterial(&mrtl);
Device->SetTexture(0,0);
Teapot->DrawSubset(0);
Device->SetRenderState(D3DRS_ZENABLE, true);
Device->SetRenderState(D3DRS_ALPHABLENDENABLE, false);
Device->SetRenderState(D3DRS_STENCILENABLE, false);
}