先上图
镜面涉及到了深度模板缓冲区。即模板运算和深度值运算都要进行
大体来说:
模板运算其实是个位运算,通过者写入模板缓存。(深度也要通过)
是谁的运算呢?
A:模板参考值&掩码
B:模板缓冲区的值&掩码
是什么样子的运算呢?
比大小,比函数,或者直接返回个bool变量
结果是什么呢?
结果,模板缓冲区的值可能不变,也可能各种变化。
模板缓冲区在哪里呢?
和深度缓冲区共享内存,第(i,j)个像素相互对应.
上面说了个大概,下一步再详细探讨下。
一、创建深度模板缓冲区
(1)深度设置:
是否启用深度测试?是否启用深度写入?测试深度的函数(一般是取最小值)?
(2)模板设置:
是否启用模版测试?读掩码?写掩码?模板缓冲区如何处理朝前的三角形和朝后的三角形?像素模版测试失败时,如何更新模板缓冲区?模版测试成功但是深度测试失败时,如何更新模板缓冲区?模版测试和深度测试成功时,如何更新模板缓冲区?模版测试使用什么比较函数?
(3)初始化时,创建深度模板状态
ID3D10DepthStencilState* mDrawMirrorDSS;
md3dDevice->CreateDepthStencilState(&dsDesc, &mDrawMirrorDSS);
ID3D10DepthStencilState* mDrawReflectionDSS;
md3dDevice->CreateDepthStencilState(&dsDesc, &mDrawReflectionDSS);
二、每帧:
1,清除深度模板缓存区
md3dDevice->ClearDepthStencilView(mDepthStencilView, D3D10_CLEAR_DEPTH|D3D10_CLEAR_STENCIL, 1.0f, 0);
2,绑定模板深度缓存状态到管线,结束时恢复
绘制镜面
md3dDevice->OMSetDepthStencilState(mDrawMirrorDSS, 1);
drawMirror(pass);
md3dDevice->OMSetDepthStencilState(0, 0);
绘制箱子的倒影
md3dDevice->OMSetDepthStencilState(mDrawReflectionDSS, 1);
mCrateMesh.draw();
md3dDevice->OMSetDepthStencilState(0, 0);
三、对于镜面来说
1,不修改模板缓冲区,把地板,墙壁,镜子和板条箱渲染到后台缓冲区
2,将模板缓冲区清0
3,把镜子渲染到模板缓冲区,成功时,模板缓冲区元素替代为1,即1是镜子区域的标志位
dsDesc.DepthEnable = true;
dsDesc.DepthWriteMask = D3D10_DEPTH_WRITE_MASK_ALL;
dsDesc.DepthFunc = D3D10_COMPARISON_LESS;
dsDesc.StencilEnable = true;
dsDesc.StencilReadMask = 0xff;
dsDesc.StencilWriteMask = 0xff;
dsDesc.FrontFace.StencilFailOp = D3D10_STENCIL_OP_KEEP;
dsDesc.FrontFace.StencilDepthFailOp = D3D10_STENCIL_OP_KEEP;
dsDesc.FrontFace.StencilPassOp = D3D10_STENCIL_OP_REPLACE;
dsDesc.FrontFace.StencilFunc = D3D10_COMPARISON_ALWAYS;
4,将板条箱影像渲染到后台缓冲区和模板缓冲区,这里有1才渲染,也就是在镜子区域才渲染。因为板条箱影像在墙壁后,所以深度测试函数要设定为始终成功,且要禁用深度写入,避免板条箱映像的深度值来更新深度缓冲区
dsDesc.DepthEnable = true;
dsDesc.DepthWriteMask = D3D10_DEPTH_WRITE_MASK_ZERO;
dsDesc.DepthFunc = D3D10_COMPARISON_ALWAYS;
dsDesc.FrontFace.StencilPassOp = D3D10_STENCIL_OP_KEEP;
dsDesc.FrontFace.StencilFunc = D3D10_COMPARISON_EQUAL;
5,对映像于镜子进行混合,颜色加权平均值作为最终颜色。
D3D10_BLEND_DESC blendDesc = { 0 };
blendDesc.AlphaToCoverageEnable = false;
blendDesc.BlendEnable[0] = true;
blendDesc.SrcBlend = D3D10_BLEND_BLEND_FACTOR;
blendDesc.DestBlend = D3D10_BLEND_INV_BLEND_FACTOR;
blendDesc.BlendOp = D3D10_BLEND_OP_ADD;
blendDesc.SrcBlendAlpha = D3D10_BLEND_ONE;
blendDesc.DestBlendAlpha = D3D10_BLEND_ZERO;
blendDesc.BlendOpAlpha = D3D10_BLEND_OP_ADD;
blendDesc.RenderTargetWriteMask[0] = D3D10_COLOR_WRITE_ENABLE_ALL;
md3dDevice->CreateBlendState(&blendDesc, &mDrawReflectionBS);
6,对j板条箱映像的三角形环绕顺序和平面法线反转。
(1)创建
D3D10_RASTERIZER_DESC rsDesc;
ZeroMemory(&rsDesc, sizeof(D3D10_RASTERIZER_DESC));
rsDesc.FillMode = D3D10_FILL_SOLID;
rsDesc.CullMode = D3D10_CULL_BACK;
rsDesc.FrontCounterClockwise = true;
md3dDevice->CreateRasterizerState(&rsDesc, &mCullCWRS);
(2)每帧渲染:
md3dDevice->RSSetState(mCullCWRS);
float blendf[] = { 0.65f, 0.65f, 0.65f, 1.0f };
md3dDevice->OMSetBlendState(mDrawReflectionBS, blendf, 0xffffffff);
md3dDevice->OMSetDepthStencilState(mDrawReflectionDSS, 1);
mCrateMesh.draw();
7,当绘制板条箱映像时,为了使影像中的光照显得真实,需要在镜子平面反射光源
D3DXPLANE mirrorPlane(0.0f, 0.0f, 1.0f, 0.0f);
D3DXMATRIX R;
D3DXMatrixReflect(&R, &mirrorPlane);
D3DXVECTOR3 oldDir = mParallelLight.dir;
D3DXVec3TransformNormal(&mParallelLight.dir, &mParallelLight.dir, &R);
mfxLightVar->SetRawValue(&mParallelLight, 0, sizeof(Light));
8,在每帧的最后,不要忘记恢复状态
md3dDevice->OMSetDepthStencilState(0, 0);
md3dDevice->OMSetBlendState(0, blendf, 0xffffffff);
md3dDevice->RSSetState(0);
mParallelLight.dir = oldDir;
注意的是,这个例子并没有在shader里更改深度模板状态。