OpenGL和D3D11中的深度模版测试
在OpenGL和D3D11的管线中,像素shader之后的操作就是深度模版测试,深度模版测试是以sample为单位进行的,就是一个像素上可以有多个采样点,每个采样点都有深度信息。深度模版测试对每个采样点都要进行一次,如果是msaa,最后要对每次采样的像素结果进行resolve,得到最终的结果。在下面的链接中有msaa的介绍。
http://www.cnblogs.com/mikewolf2002/archive/2012/11/22/2783235.html
深度模版测试的流程如下图:
在OpenGL中使用深度模版测试,首先要enable深度测试和模版测试,可以通过下面两个函数实现:
glEnable(GL_DEPTH_TEST);
glEnable(GL_STENCIL_TEST);
下面的函数初始化深度缓冲和模版缓冲值。
glClearStencil(0); // 初始为0
glClearDepth(1.0f); //初始为1.0
glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); //该函数会复位深度和模版值,分别为1.0和0,就是每个像素对应的深度值初始为1.0,对应的模版值为0。
在模板测试的过程中,可以先使用一个比较用掩码(comparison mask)与模板缓冲区中的值进行位与运算,再与参考值进行比较,从而实现对模板缓冲区中的值的某一位上的置位状态的判断。这样,模板缓冲区中的值不仅可以作为一个独立的整体使用,还可以作为一个比特集合使用。
在OpenGL中,可以通过调用glStencilFunc()函数来设定比较条件(comparison function)、参考值(reference value)以及比较用掩码(comparison mask)。
glStencilFunc(GL_EQUAL, // 比较条件
0x1, // 参考值
0xff); // 比较用掩码
比较条件可以设置为以下的值
GL_NEVER 从来不能通过
GL_ALWAYS 永远可以通过(默认值)
GL_LESS 小于参考值可以通过
GL_LEQUAL 小于或者等于可以通过
GL_EQUAL 等于通过
GL_GEQUAL 大于等于通过
GL_GREATER 大于通过
GL_NOTEQUAL 不等于通过
除了比较参考值与模板值之外,我们还需要使用一些操作来更新模板缓冲区中的值,这些操作被称为模板操作(stencil operation)。模板缓冲区的更新与模板测试的结果以及深度测试的结果有着密切的联系。如上面的流程图所示:模板操作可以为下述三种情况,分别指定相应的更新方法。
1. 模板测试失败。
2. 模板测试通过,但深度测试失败。
3. 模板测试通过,且深度测试通过。
当上述情况中的一个发生时,就会执行预先设定的更新操作。在OpenGL中,可以使用glStencilOp()函数来为上述三种情况分别设置更新方法。例如,
glStencilOp(GL_KEEP, // 第一种情况更新方法
GL_DECR, // 第二种情况的更新方法
GL_INCR); // 第三种情况的更新方法
可是设置的更新方法如下:
GL_KEEP 保持当前的模板值不变
GL_ZERO 将当前的模板值设为0
GL_REPLAC 将当前的模板值设置为参考值
GL_INCR 在当前的模板值上加1
GL_DECR 在当前的模板值上减1
GL_INVERT 对当前的模板值进行按位取反操作
我们可以通过写入掩码(write mask)来更新模板值指定比特位上的置位状态。OpenGL中,提供了glStencilMask()函数来设置写入掩码。例如,glStencilMask(0xff);还可以通过函数glDepthMask(GL_FALSE)来打开或关闭深度写功能,GL_TRUE为打开,GL_FALSE为禁止写。在新版本的OpenGL中,允许为多边形的正面和背面使用不同的模板测试条件和模板值改变方式,于是就有了glStencilFuncSeparate函数和glStencilOpSeparate函数。这两个函数分别与glStencilFunc和glStencilOp类似,只在最前面多了一个参数face,用于指定当前设置的是哪个面。可以选择GL_FRONT, GL_BACK, GL_FRONT_AND_BACK。
在D3D11中,通过深度模版状态来设置深度模版测试各种条件。
// 设置深度模版状态描述.
depthStencilDesc.DepthEnable = true;
depthStencilDesc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ALL;//D3D11_DEPTH_WRITE_MASK_ZERO禁止写深度缓冲
depthStencilDesc.DepthFunc = D3D11_COMPARISON_LESS;
depthStencilDesc.StencilEnable = true;
depthStencilDesc.StencilReadMask = 0xFF; //和OpenGL中函数glStencilFunc的第三个参数意思一样
depthStencilDesc.StencilWriteMask = 0xFF; //和glStencilMask意思一样
// 对于front face 像素使用的模版操作操作,三种状况下的stencil操作
depthStencilDesc.FrontFace.StencilFailOp = D3D11_STENCIL_OP_KEEP;
depthStencilDesc.FrontFace.StencilDepthFailOp = D3D11_STENCIL_OP_INCR;
depthStencilDesc.FrontFace.StencilPassOp = D3D11_STENCIL_OP_KEEP;
depthStencilDesc.FrontFace.StencilFunc = D3D11_COMPARISON_ALWAYS;
// 对于back face像素使用的模版操作模式.
depthStencilDesc.BackFace.StencilFailOp = D3D11_STENCIL_OP_KEEP;
depthStencilDesc.BackFace.StencilDepthFailOp = D3D11_STENCIL_OP_DECR;
depthStencilDesc.BackFace.StencilPassOp = D3D11_STENCIL_OP_KEEP;
depthStencilDesc.BackFace.StencilFunc = D3D11_COMPARISON_ALWAYS;
// 创建深度模版状态,使其生效
result = m_device->CreateDepthStencilState(&depthStencilDesc, &m_depthStencilState);
if(FAILED(result))
{
return false;
}
// 设置深度模版状态.
m_deviceContext->OMSetDepthStencilState(m_depthStencilState, 1);