深度图及后处理中用深度图重建世界坐标

深度图介绍

就是将深度信息(Z坐标值)保存在了一张贴图上的R通道上,因为R通道的值范围是[0, 1],所以我们可以用ndc空间下的Z坐标值做下处理就能变成[0, 1]范围了( (Zndc+1)*0.5 )

 

如何查看深度图

1) 使用Frame Debug查看,Window -> Analysis -> Frame Debugger

注意要设置Camera的深度图模式属性:m_Camera.depthTextureMode = DepthTextureMode.Depth

2) 使用Shader.GetGlobalTexture("_CameraDepthTexture")获取深度图后,用RawImage展示,把下面的脚本挂Camera上

using UnityEngine;
using UnityEngine.UI;

[RequireComponent(typeof(Camera))]
public class CameraUseDepthTex : MonoBehaviour
{
    private Camera m_Camera;
    public RawImage m_rawImg;

    private void Start()
    {
        m_Camera = GetComponent<Camera>();
        m_Camera.depthTextureMode = DepthTextureMode.Depth;
    }

    void OnPostRender()
    {
        var tex = Shader.GetGlobalTexture("_CameraDepthTexture");
        if (null != tex)
        {
            //Debug.Log($"{tex.GetType().Name}");
            if (null != m_rawImg)
            {
                m_rawImg.texture = tex;
                m_rawImg.SetNativeSize();
            }
        }
    }

}

上面的2个深度图可以模糊的看到一个正方体和胶囊体的画面,让人都怀疑深度图到底有没有生成。

 

深度图是否明显与near值有关

1) near值太小就会不明显,上面就是因为相机的Clipping Planes的near值设置太小了

2) 深度计算公式

其中:near为近平面的值,far为远平面的值,Zndc表示ndc空间下的Z坐标,Zcam表示视角空间下的Z坐标

而在DirectX 11,DirectX 12等平台上还需要再做下处理

depth=1-Zndc,至于为啥要这样处理,主要是浮点数精度问题会引发Z-Fight闪烁,具体的自己百度。

a) near设为0.3时,基本Zcam超过5之后,值就接近0了

b) near设为3就好很多了

c) near设为3时的深度图,就很明显了

 

 

 后处理中用深度图重建世界坐标

using UnityEngine;

[RequireComponent(typeof(Camera))]
public class PostEffectUseDepthTex : MonoBehaviour
{

    private Camera m_Camera;
    private Material m_GetWorldPosByDepthMat;

    void Start()
    {
        m_Camera = GetComponent<Camera>();
        m_GetWorldPosByDepthMat = new Material(Shader.Find("My/DepthTex/GetWorldPosByDepth"));
    }

    void OnRenderImage(RenderTexture src, RenderTexture dst)
    {
        var VPMatrix = m_Camera.projectionMatrix * m_Camera.worldToCameraMatrix; // World -> View -> Projection
        var clipToWorldMatrix = VPMatrix.inverse;
        m_GetWorldPosByDepthMat.SetMatrix("_ClipToWorldMatrix", clipToWorldMatrix);
        Graphics.Blit(src, dst, m_GetWorldPosByDepthMat);
    }

}

 用到的shader

//后处理中根据深度图重建世界坐标, 注意: 只适用于后处理OnRenderImage那边
Shader "My/DepthTex/GetWorldPosByDepth"
{
    Properties
    {
        _MainTex("Texture", 2D) = "white" {}
    }

    SubShader
    {
        ZTest Always
        ZWrite Off

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"

            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };

            struct v2f
            {
                float4 vertex : SV_POSITION;
                float2 uv : TEXCOORD0;
                float2 uv_depth : TEXCOORD1;
            };

            sampler2D _CameraDepthTexture; //深度图
            sampler2D _MainTex; //后处理提供的屏幕图
            float4 _MainTex_TexelSize;
            float4x4 _ClipToWorldMatrix; //裁剪空间到世界空间转换矩阵, 需要c#部分提供

            v2f vert(appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);

                o.uv = v.uv;

                o.uv_depth = v.uv;
                #if UNITY_UV_STARTS_AT_TOP
                if (_MainTex_TexelSize.y < 0)
                o.uv_depth.y = 1 - o.uv_depth.y;
                #endif

                return o;
            }

            fixed4 frag(v2f i) : SV_Target
            {
                float depth = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, i.uv_depth);
                #if defined(UNITY_REVERSED_Z)
                depth = 1 - depth;
                #endif
                float4 ndcPos = float4(i.uv.x * 2 - 1, i.uv.y * 2 - 1, depth * 2 - 1, 1); //后处理的那张贴图就对应整个屏幕, 所以可以直接用像素创建ndc坐标
                float4 worldPos = mul(_ClipToWorldMatrix, ndcPos); //ndcPos和clipPos只是差一个倍数(就是w分量)
                worldPos /= worldPos.w;

                //用世界坐标做相关处理......

                fixed4 c = tex2D(_MainTex, i.uv);
                return c;
            }
            ENDCG
        }
    }

    FallBack Off
}

当然啦,这边拿到世界坐标后啥都没做,所以画面看不到任何实际变化。至于还原了世界坐标能做什么,比如:雾效,运动模糊等。

 

 

参考

posted @ 2023-03-22 23:25  yanghui01  阅读(736)  评论(0编辑  收藏  举报