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,);
}
}
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
-
SetDepthBoundTest(.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
-
Vec4 pDepthBounds =GetLightDepthBounds( pDL );
rd->SetDepthBoundTest( pDepthBounds.x, pDepthBounds.z,true);IBL的延迟着色
CDeferredShading::DeferredCubemapPass
-
Vec4 pDepthBounds =GetLightDepthBounds( pDL );
rd->SetDepthBoundTest( pDepthBounds.x, pDepthBounds.z,true);屏幕空间反射
CDeferredShading::ScreenSpaceReflectionPass
-
rd->SetDepthBoundTest(0.0f,0.9999f,true);
CDeferredShading::DirectionalOcclusionPass()
-
CDeferredShading::DirectionalOcclusionPass()
阴影处理
CDeferredShading::ShadowLightPasses()
-
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替代,但这两种方法在移动平台上忌讳的,所以需要进一步测试才有结果了