Stenciling技术实现镜子效果(二)
tRenderState(D3DRS_ZWRITEENABLE, false);//关闭depth buffer写入
gd3dDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, true);//开启blending
gd3dDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_ZERO);//设置源像素因子(就是将要写入的像素)为0,那么与像素值与之后,结果为零。
gd3dDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_ONE);//目标(原来在back buffer的)像素因子为1, 与目标像素与之后,结果为目标像素值
// Draw mirror to stencil only.
drawMirror();
// Re-enable depth writes
HR(gd3dDevice->SetRenderState( D3DRS_ZWRITEENABLE, true));
3.设置映射渲染需要的状态:
[cpp]
// Only draw reflected teapot to the pixels where the mirror was drawn.
gd3dDevice->SetRenderState(D3DRS_STENCILFUNC, D3DCMP_EQUAL);
gd3dDevice->SetRenderState(D3DRS_STENCILPASS, D3DSTENCILOP_KEEP);
这时候如果stencil测试通过对的话,就保持需要画的物体的像素。测试函数为D3DCMP_EQUAL,而镜子的stencil值为1,测试的值也为1,mask的值为0Xffffffff,所以镜子区域的像素都为通过状态,就是说这个状态下,物体就能在镜子映射出来。
4.下面就是正式画镜子物体的步骤了,以画一个茶壶为例:
[cpp]
// Build reflection transformation.
D3DXMATRIX R;
D3DXPLANE plane(0.0f, 0.0f, 1.0f, 0.0f); // xy-plane
D3DXMatrixReflect(&R, &plane);
// Save the original teapot world matrix.
D3DXMATRIX oldTeapotWorld = mTeapotWorld;
// Add reflection transform.
mTeapotWorld = mTeapotWorld * R;
// Reflect light vector also.
D3DXVECTOR3 oldLightVecW = mLightVecW;
D3DXVec3TransformNormal(&mLightVecW, &mLightVecW, &R);
mFX->SetValue(mhLightVecW, &mLightVecW, sizeof(D3DXVECTOR3));
这里是坐标变换的知识,利用原有的茶壶位置和反射平面的位置计算出需要映射茶壶的位置,然后渲染之。
5. 最后一步了
如果我们现在马上渲染茶壶的话,那反射镜子里面不会显示茶壶的。为什么?因为镜子离镜头更加近,所以把茶壶挡住了,这时候需要关闭深度测试。
注意:关闭深度测试的时候,画物体的先后顺序会很重要,画后的物体会遮蔽画先的物体,就是相当于把先画在back buffer的像素复写了。所以这里一定要画完镜子,然后再画镜子里面的物体,否则,镜子就挡住了里面的物体了。
[cpp]
// Disable depth buffer and render the reflected teapot. We also
// disable alpha blending since we are done with it.
gd3dDevice->SetRenderState(D3DRS_ZENABLE, false);
gd3dDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, false);
gd3dDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_CW);
drawTeapot();
用D3DCULL_CW,意思就是顺时针为正面,directx默认是逆时针为正面的, 因为在镜子里面是反射世界,所以正面和反面都调转过来了。
6.还有就是还原为默认渲染状态的代码了:
[cpp]
// Restore original teapot world matrix and light vector.
mTeapotWorld = oldTeapotWorld;
mLightVecW = oldLightVecW;
// Restore render states.
gd3dDevice->SetRenderState(D3DRS_ZENABLE, true);
gd3dDevice->SetRenderState( D3DRS_STENCILENABLE, false);
gd3dDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_CCW);
总结:
这里的渲染知识要一定基础,包括:
1. 坐标转换
2.几何知识
3.渲染过程一定了解
DirectX感觉还是挺方便的了,如果要更好的理解也许学习一下最基本的一些图形学知识会更加好吧。比如一些图形学最低层的算法,如何操作像素,一些画直线的算法和众多插值算法(这是很博大精深的算法),这些东西很低层,不过对于理解高层的应用还是非常有用的。