关于Depth Bounds Test (DBT)和在CE3的运用

Depth Bounds Test (DBT)
Depth Bounds Test(深度范围检测),是Nvdia GeForce 6系列以后显卡的特性(GPU Programming Guide GeForce 8 and 9 Series),并不是DirectX的特性。所以在例如Nsight和Pix的图形分析工具里,是看不到它的设置的。
 
Depth Bounds Test的功能是允许程序员在blend render target前进行额外的像素Discard。这个扩展增加了一个新的逐个fragment 的测试,从逻辑上讲,它是在scissor test的后面,alpha test的前面,DBT会把保存在输入的fragment 坐标(xw,yw)位置的深度值,和用户定义的最大和最小深度值做比较,如果保存的深度值在用户定义的范围外的话,那么这个输入的fragment 会被discard掉,和alpha test不同的是,DBT并不依赖fragment在窗口空间的深度值。
DBT并不依赖从Pixel Shader输出的深度值,而是根据之前保存在render target里的深度位置做判断
  • DBT的逻辑是在 scissor test 后面, alpha testing前面
  • min/max值被clamp在[0, 1]之间,非法值会被转为0
  • DBT会被打开,当下面条件都为真时
    • min < max
    • min > 1 并且 max<1
    • depth值在Shader里被使用
  关于API使用,以CE3为例的话
void CD3D9Renderer::SetDepthBoundTest(float fMin,float fMax,bool bEnable)
{
    if(bEnable)
    {
        m_pd3dDevice->SetRenderState(D3DRS_ADAPTIVETESS_X,MAKEFOURCC('N','V','D','B'));
        m_pd3dDevice->SetRenderState(D3DRS_ADAPTIVETESS_Z,*(DWORD*)&fMin);
        m_pd3dDevice->SetRenderState(D3DRS_ADAPTIVETESS_W,*(DWORD*)&fMax);
  }
    else// disable depth bound test
    {
        m_pd3dDevice->SetRenderState(D3DRS_ADAPTIVETESS_X,0);
    }
}  

 

  DBT的主要用处有两个,一个例子是在OpenGL extension文档里的例子 可以用来优化stenciled shadow volume 的渲染

  例如,一栈带有衰减等光源,可以在XY窗口空间被边界为矩形,而传统的scissor test,可以把矩形内的 shadow volume 的fragment都discard掉,保证阴影是在光源的windows窗口XY范围外的。
  而stenciled 增长或减少的部分偏离scissor的fragment的时候就无关紧要了,因为光源的照明在scissor区域外的部分,已经可以确实是完全衰减消失的了。
 
  也就是说,scissor test可以把在渲染在scissor外面的 shadow volume fragments 直接discard掉,从而提高了性能,不再需要关注那些衰减的光源最终不会影像的像素。Scissor的优化,可以用在更新渲染用的stenciled shadow volumes的时候(增加或减少stencil buffer),和增加带衰减光源的照明分布的时候。
 
  使用DBT时,我们也可以使用类似的方法,计算出衰减光源的最终照明信息在窗口空间上的Z边界(zmin,zmax),除非像素的深度值在这个范围[zmin, zmax],否则光源的照明可以被预确定是和这个像素是无关的。或者说,被做照明处理的像素在衰减光源足够远的前方或后方,以至于光源在这个像素上的照明完全的衰减没了。而DBT恰好可以做这种测试。
 
  另外一个用处也就是像CE3这种延迟渲染中了,因为光照是转化到屏幕空间进行的,所以在对物体进行光照处理时,可以参考之前的写入的光照深度,屏幕空间上,不在光照深度范围内的像素都DBT裁剪掉。
 
  在CE3.4里,主要还是在一些平面空间处理里使用,这样可以省去alpha test步骤以及额外的像素填充
 

生成SceneDiffuseAcc,使用DBT就可以省去天空部分像素判断和填充了
 
生成AO
CD3D9Renderer::GenerateAO
  1. SetDepthBoundTest(0.f,0.9999f,true);

    下雨效果
    CD3D9Renderer::FX_DeferredRainLayer()

constVec4 vDepthBounds =CDeferredShading::Instance().GetLightDepthBounds(rainVolParams.m_vWorldPos, rainVolParams.m_fRadius);
SetDepthBoundTest(max(vDepthBounds.x, fMinZ), min(vDepthBounds.z, fMaxZ),true);

下雪效果
CD3D9Renderer::FX_DeferredSnowLayer()

constVec4 vDepthBounds =CDeferredShading::Instance().GetLightDepthBounds(snowVolParams.m_vWorldPos, snowVolParams.m_fRadius);
SetDepthBoundTest(max(vDepthBounds.x, fMinZ), min(vDepthBounds.z, fMaxZ),true);

延迟光照着色
CDeferredShading::LightPass

  1. Vec4 pDepthBounds =GetLightDepthBounds( pDL );
    rd->SetDepthBoundTest( pDepthBounds.x, pDepthBounds.z,true);

    IBL的延迟着色
    CDeferredShading::DeferredCubemapPass

  1. Vec4 pDepthBounds =GetLightDepthBounds( pDL );
    rd->SetDepthBoundTest( pDepthBounds.x, pDepthBounds.z,true);

    屏幕空间反射
    CDeferredShading::ScreenSpaceReflectionPass 

  1. rd->SetDepthBoundTest(0.0f,0.9999f,true);

    CDeferredShading::DirectionalOcclusionPass()

  2. CDeferredShading::DirectionalOcclusionPass()

    阴影处理
    CDeferredShading::ShadowLightPasses()

  1. Vec4 pDepthBounds =GetLightDepthBounds(&light );
    rd->SetDepthBoundTest( pDepthBounds.x, pDepthBounds.z,true);

    为了在绘制里可以忽略天空部分
    void CDeferredShading::Render()

 
LPV光照
CRELightPropagationVolume::DeferredApply()
if(CRenderer::CV_r_DeferredShadingDepthBoundsTest )
{
    constVec4 pDepthBounds =CDeferredShading::Instance().GetLightDepthBounds( rRendSettings.m_pos, m_fDistance );
    rd->SetDepthBoundTest(pDepthBounds.x, pDepthBounds.z,true);// skip sky only for GI
}

修改方法也比较简单,关闭CV_r_DeferredShadingDepthBoundsTest,或在shader里添加clip来discard掉像素,或用alpha test替代,但这两种方法在移动平台上忌讳的,所以需要进一步测试才有结果了

posted @ 2014-11-13 21:00  Trace0429  阅读(2126)  评论(2编辑  收藏  举报