深度图重建世界空间是从深度信息计算出对应像素的世界位置坐标,对于实现如Raymarching、阴影映射、环境遮挡和后处理效果(例如景深和运动模糊)等高级图形效果至关重要。

NDC变换法

在该方法中,需要先构建出NDC坐标,再通过VP矩阵(视图矩阵和透视矩阵)将NDC坐标变换到世界空间。

深度图重建世界空间:NDC变换法
在3D渲染中,若任意两个点在最终渲染图像里对应着同一个像素点,即两点的屏幕空间坐标完全一致,那么无论采用的是透视投影还是正交投影方式,该两点在NDC空间下的X、Y坐标值必然相同。在进行深度图重建世界空间时,采样深度图所使用的坐标恰好是归一化屏幕空间坐标(屏幕UV),那么我们便可据此计算出采样到的深度值对应点的X、Y坐标。

  1. 首先将屏幕UV转到NDC空间(映射到[-1, 1])得: $$ \text{NDC}_{XY} = 2 \cdot \text{UV}_\text{screen} - 1 $$
  2. 构建NDC空间下的Z值,并补全W分量: $$ \begin{align*} \text{NDC}_{Z} &= \begin{cases} \text{Depth}, & \text{DirectX} \\ 2 \cdot \text{Depth} + 1, & \text{OpenGL} \end{cases} \\[10pt] \text{NDC}_{W} &= 1.0 \end{align*} $$
  3. NDC空间世界空间的齐次坐标(视图矩阵 & 透视矩阵 & 齐次除法): $$ \begin{align*} \text{Position}_\text{WSHC} &= M_{\text{View}}^{-1}\cdot M_{\text{Project}}^{-1}\cdot\text{NDC} \\[10pt] \text{Position}_{XYZ} &= \frac{(\text{Position}_\text{WSHC})_{XYZ}}{(\text{Position}_\text{WSHC})_{W}} \end{align*} $$

Unity示例如下

HLSL
half4 frag(Varyings IN) : SV_Target {
	// 采样深度图
	real rawDepth = SampleSceneDepth(IN.uv);
	
	// 转为平台规范的 NDC Z
	#if UNITY_REVERSED_Z
		// D3D/Vulkan: Reverse-Z,rawDepth [0,1] 对应 [远, 近]
		// NDC Z 需要映射到 [0,1]
		float ndcZ = rawDepth;
	#else
		// OpenGL: 传统 Z,rawDepth [0,1] 对应 [近, 远]
		// NDC Z 需要映射到 [-1,1]
		float ndcZ = lerp(UNITY_NEAR_CLIP_VALUE, 1.0, rawDepth);
	#endif
	
	// 重建世界空间位置
	float3 worldPos = ComputeWorldSpacePosition(IN.uv, ndcZ, UNITY_MATRIX_I_VP);
}
点击展开查看更多

大部分逻辑已经被封装成函数ComputeWorldSpacePosition

远裁剪平面法(相似三角形法)

深度图重建世界空间:远裁剪平面法

  1. 首先将屏幕UV转到NDC空间(映射到[-1, 1])得: $$ \text{NDC}_{XY} = 2 \cdot \text{UV}_\text{screen} - 1 $$
  2. 假设点在远裁剪平面上,构建NDC空间下的Z值,并补全W分量: $$ \begin{align*} \text{NDC}_{Z} &= \begin{cases} 0, & \text{DirectX} \\ 1, & \text{OpenGL} \end{cases} \\[10pt] \text{NDC}_{W} &= 1.0 \end{align*} $$
  3. 求该假设的远裁剪平面上的点的世界坐标: $$ \begin{align*} \text{Position}_{\text{远}_\text{WSHC}} &= M_{\text{View}}^{-1}\cdot M_{\text{Project}}^{-1}\cdot\text{NDC} \\[10pt] \text{Position}_{\text{远}_{XYZ}} &= \frac{(\text{Position}_{\text{远}_\text{WSHC}})_{XYZ}}{(\text{Position}_{\text{远}_\text{WSHC}})_{W}} \end{align*} $$
  4. 根据深度插值出最终坐标: $$ \text{Position} = \text{Lerp}(\text{Position}_{\text{相机}_{XYZ}}, \; \text{Position}_{\text{远}_{XYZ}}, \; \text{Linear01Depth}) $$

解释:当深度0时,说明最终坐标正好在相机的位置;当深度1时,说明最终坐标在远裁剪平面上。

Unity示例如下

HLSL
Varyings vert(Attributes IN) {
    Varyings OUT;
    // 调用 URP 内置函数,根据 ID 计算裁剪空间坐标和 UV
    OUT.positionCS = GetFullScreenTriangleVertexPosition(IN.vertexID);
    OUT.uv = GetFullScreenTriangleTexCoord(IN.vertexID);

    // ---- 核心:在 VS 构造射线 ----
    
    // 获取远裁剪面的 NDC Z
    #if UNITY_REVERSED_Z
	    // D3D/Vulkan: Reverse-Z,rawDepth [0,1] 对应 [远, 近]
        float farZ = 0.0;
    #else
	    // OpenGL: 传统 Z,rawDepth [0,1] 对应 [近, 远]
        float farZ = 1.0;
    #endif
    
    /*
    // 从 UV 还原 NDC 的 XY 坐标 [-1, 1]
    float2 ndcXY = OUT.uv * 2.0 - 1.0;
    #if UNITY_UV_STARTS_AT_TOP
	    ndcXY.y = -ndcXY.y;
	#endif
    // 构建远裁剪面上的点
    float4 farClipPos = float4(ndcXY, farZ, 1.0);
    // 裁剪空间转观察齐次空间
    float4 farClipPosWS = mul(UNITY_MATRIX_I_VP, farClipPos);
    // 顶点数越少,这里的透视除法执行得也就越少,极赚!
    farClipPosWS.xyz /= farClipPosWS.w;
    OUT.farClipPosWS = farClipPosWS.xyz;
    */
    // 运算逻辑一样
    OUT.farClipPosWS = ComputeWorldSpacePosition(OUT.uv, farZ, UNITY_MATRIX_I_VP);
    return OUT;
}

half4 frag(Varyings IN) : SV_Target {
	// 采样深度图
	real rawDepth = SampleSceneDepth(IN.uv);
	float linear01 = Linear01Depth(rawDepth, _ZBufferParams);

	// 根据深度插值出最终坐标
	float3 worldPos = lerp(_WorldSpaceCameraPos, IN.farClipPosWS, linear01);
}
点击展开查看更多

两种方法的对比

这两种方法的运算逻辑实际上基本上是一样的,远裁剪平面法更像是NDC变换法的一种优化手段

计算量对比

  • NDC变换法中,所有的计算均在片元着色器中进行,故其计算压力像素数量正相关;
  • 远裁剪平面法的大部分计算都在顶点着色器中进行,故其计算压力顶点数量正相关。 深度图重建世界空间的大多数应用所使用Mesh的顶点并不多,如后处理所使用顶点就极少,在这种情况下远裁剪平面法的计算量比NDC变换法的少了不是一星半点。

精度问题

远裁剪平面法是通过计算顶点上的数据,再通过插值传到片元。在后处理场景理论上不会有什么损失,但其他场景可能会产生损失。

版权声明

作者: Cheyne Xie

链接: https://chaim.eu.org/posts/084fc0b8/

许可证: CC BY-NC-SA 4.0

This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License. Please attribute the source, use non-commercially, and maintain the same license.

开始搜索

输入关键词搜索文章内容

↑↓
ESC
⌘K 快捷键