• D3D depth buffer的预览


    在使用D3D开发游戏的过程中,很多情况下都会用到depth buffer来完成特定的效果,比如DOF,Shadows,SSAO等等。在这些情况下我们就可能需要预览depth buffer来确定它是正确的,以免导致后续运算渲染出错。此时有一个问题就出现了,因为原始的depth buffer中保存的depth不是线性的。我们知道,世界坐标系下的顶点在经过视图变换后会被转换到视图空间,此时摄像机在原点,并且我们为摄像机定义了一个近平面和一个远平面:

    在经过视图变换后,接着执行的就是投影变换,D3D的透视投影变换矩阵如下,其中,zn就是为摄像机定义的近平面,而zf 就是远平面:

    这里,我们因为在讨论depth,所以我们只关心z的变换。z在经过透视投影矩阵的洗礼后,得到的是:

    (公式中的z是顶点坐标变换到视图坐标系后的z)

    此时的g(z)还不是真正保存到depth buffer中的值,还需要经过perspective divide,也就是我们常说的投影变换后需要进行的步骤:(x, y, z) / w。

    最后,我们得到的才是真正保存到depth buffer中的值,他看起来是这样的:

       公式1

    根据这个公式,我们来看下图,图中我绘制了两条曲线图,其中一条我们定义近平面zn的值为1,远平面zf 的值为100。而另一条我们定义近平面zn的值为10,远平面zf 的值为100:

    此时我们就可以发现,随着z的变化,g(z)并不是线性变化的,靠近近平面的很少一部分z值在变换后却占据了绝大部分的g(z)范围,而很大一部分z值在变换后却需要去争抢剩余的很小一部分g(z)范围(题外话:这也就是导致depth buffer精度问题的原因,以及为什么我们常说把近平面和远平面设置的更加靠近可以减少depth buffer的精度问题)。所以当我们在预览depth buffer时,看到的很大情况下都是白茫茫一片,因为绝大部分z值在变换后都位于g(z)的那一小部分范围内(也就是接近1.0的范围)。

    那么我们该如何正确的预览depth buffer呢,没错,就是把depth转换回线性的!

    那么该如何转换回线性depth值呢,因为顶点从世界坐标系变换到视图坐标系后,此时的z值仍是线性的,所以我们要做的就是将depth值倒推回它在视图空间的z值。根据公式1,我们来计算视图空间的z值:

    现在,我们就得到了视图空间的z值,这个值是线性的。但是,我们还不能用这个值来预览depth,我们还必须把它归一化到区间[0, 1],然后才可以存到render target中用于预览。那么如何把视图空间的z值归一化到[0, 1]呢?因为在视图空间中,摄像机位于原点,并且顶点在可见时它的z值会落在区间[zn, zf],所以我们要做的就是把区间[zn, zf]转换到[0, 1],也就是执行操作:(z - zn) / (zf - zn):

    好了,至此,我们已经得到了线性的depth,并且它的值是位于区间[0, 1]的。最后,我们只需要把这个值写入render target进行预览就可以了。

    下面展示我写的一个预览depth buffer的Demo,部分核心shader代码如下:

    //--------------------------------------------------------------------------------------
    // Constant buffers
    //-------------------------------------------------------------------------------------
    cbuffer ViewDepthPerFramePS : register( b0 )
    {
        float2 f2ClipPlane;     // f2ClipPlane.x stores the near plane and f2ClipPlane.y stores the far plane
    }
    
    //--------------------------------------------------------------------------------------
    // Textures and sampler states
    //--------------------------------------------------------------------------------------
    Texture2D    DebugTexture	: register( t0 );
    SamplerState PointSampler	: register( s0 );
    
    float4 DebugOutputViewDepthPS( VS_OUTPUT pInput ) : SV_TARGET
    { 
        float nonLinearizeDepth = DebugTexture.Sample( PointSampler, pInput.texCoords );
        float linearizeDepth = (f2ClipPlane.x * nonLinearizeDepth) / (f2ClipPlane.y - nonLinearizeDepth*(f2ClipPlane.y - f2ClipPlane.x));
    
        return float4( linearizeDepth, linearizeDepth, linearizeDepth, 1.0f );
    }

     最终效果如图:

  • 相关阅读:
    angualr清除定时器
    flex布局常用属性
    将数组分割为几个等长度的子数组(使用slice)
    vue+element搭建的后台管理系统
    世界这么大,我想去看看!!!
    angularJs自定义指令(directive)实现滑块滑动
    适用初学者:vue2.0构建单页应用最佳实战
    笔记本win10安装node的尖酸历程。。。。。。
    js对数组处理(数组里边相同元素提取成map)
    快速搞定用Vue+Webpack搭建前端项目(学习好久了,该写点东西了......)
  • 原文地址:https://www.cnblogs.com/twjcnblog/p/3721873.html
Copyright © 2020-2023  润新知