Unity Shader 深度重建世界坐标

只用一张深度图就能还原每个像素对应的世界空间位置:用 NDC 坐标 + 逆 VP 矩阵反算。这是 SSAO、SSR、体积雾等所有屏幕空间效果的底层基础。

一、核心原理

当我们渲染一个 3D 场景时,GPU 会将顶点从世界空间 变换到屏幕空间 ,这个过程涉及 View 矩阵和 Projection 矩阵。深度重建的本质就是反向这个过程。

Pworld = (VP)-1 × Pndc

世界坐标 = 逆 View-Projection 矩阵 × 标准化设备坐标

为什么不用线性深度? 透视投影的非线性深度分布使得直接反算不可行。NDC 深度值 [0,1] 对应的是经过透视除法的齐次坐标,只有逆 VP 矩阵才能正确还原原始的 3D 位置。

二、完整实现代码

下面是一个完整的 Shader 实现,使用 URP 的内置函数和属性来重建世界坐标。

cs 复制代码
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;
public class DepthReconstructionFeature : ScriptableRendererFeature
    DepthReconstructionPass m_ScriptablePass;
    public override void Create()
    {
        m_ScriptablePass = new DepthReconstructionPass();
        m_ScriptablePass.renderPassEvent = RenderPassEvent.AfterRenderingOpaques;
    }
    public override void AddRenderPasses(ScriptableRenderer renderer,
        ref RenderingData renderingData)
    {
        m_ScriptablePass.Setup(renderer.cameraDepthTexture);
        renderer.EnqueuePass(m_ScriptablePass);
    }
}
cs 复制代码
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;
public class DepthReconstructionFeature : ScriptableRendererFeature
    DepthReconstructionPass m_ScriptablePass;
    public override void Create()
    {
        m_ScriptablePass = new DepthReconstructionPass();
        m_ScriptablePass.renderPassEvent = RenderPassEvent.AfterRenderingOpaques;
    }
    public override void AddRenderPasses(ScriptableRenderer renderer,
        ref RenderingData renderingData)
    {
        m_ScriptablePass.Setup(renderer.cameraDepthTexture);
        renderer.EnqueuePass(m_ScriptablePass);
    }
}
DepthReconstructionPass.cs
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;
public class DepthReconstructionPass : ScriptableRenderPass
    private Shader m_Shader;
    private Material m_Material;
    private RTHandle m_DepthTexture;
    public void Setup(RTHandle depthTexture)
    {
        m_DepthTexture = depthTexture;
        m_Shader = Shader.Find("Hidden/DepthReconstruction");
    }
    public override void Execute(ScriptableRenderContext context,
        ref RenderingData renderingData)
    {
        if (m_Shader == null) return;
        
        if (m_Material == null)
            m_Material = new Material(m_Shader);
        ref CameraData cameraData = ref renderingData.cameraData;
        Matrix4x4 invVP = cameraData.camera.projectionMatrix
                         * cameraData.camera.worldToCameraMatrix;
        invVP = invVP.inverse;
        
        m_Material.SetMatrix("_InvVP矩阵", invVP);
        m_Material.SetTexture("_CameraDepthTexture", m_DepthTexture);
        
        // 在此处绘制全屏Quad进行深度重建
    }
}
cs 复制代码
Shader "Hidden/DepthReconstruction"
    Properties
    {
        _MainTex ("Screen Texture", 2D) = "white" {}
    }
    CGINCLUDE
    #include "UnityCG.cginc"
    
    // ============================================
    // 核心方法:从深度图重建世界坐标
    // ============================================
    float3 ReconstructWorldPosition(float2 uv, float rawDepth)
    {
        // 步骤1:构建 NDC 坐标
        // 将屏幕UV转换为NDC空间 [-1, 1]
        float2 ndc = uv * 2.0 - 1.0;
        
        // 步骤2:构造齐次坐标
        // NDC_z 已经经过透视除法,需要恢复为 clip space
        float4 clipPos = float4(ndc.x, ndc.y, rawDepth * 2.0 - 1.0, 1.0);
        
        // 步骤3:逆变换到世界空间
        // 乘以逆 VP 矩阵
        float4 worldPos = mul(_InvVP, clipPos);
        
        // 步骤4:透视除法
        // w 分量保存原始深度信息,除以它得到真实坐标
        worldPos /= worldPos.w;
        
        return worldPos.xyz;
    }
    ENDCG

三、矩阵变换详解

理解 VP 矩阵及其逆矩阵是掌握深度重建的关键。下面的流程图展示了完整的变换链路:

1

世界矩阵 (M)

模型自身变换

2

视图矩阵 (V)

相机坐标系变换

3

投影矩阵 (P)

透视/正交投影

**性能提示:**逆 VP 矩阵可以在 C# 端预计算并传递给 Shader,避免在 GPU 上进行昂贵的矩阵求逆运算。

矩阵 作用 输入空间 输出空间
View (V) 将世界坐标转换到相机视角 World Space View Space
Projection (P) 将视图坐标投影到裁剪空间 View Space Clip Space
VP 组合变换,一步到位 World Space Clip Space
(VP)⁻¹ 逆向重建世界坐标 NDC Space World Space

四、应用场景

深度重建是众多屏幕空间技术的基石。掌握这项技术后,你可以实现以下效果:

SSAO

Screen Space Ambient Occlusion

屏幕空间环境光遮蔽

SSR

Screen Space Reflections

屏幕空间反射

体积雾

Volumetric Fog

基于深度的雾效计算

SSAO 实现要点

cs 复制代码
性能提示:逆 VP 矩阵可以在 C# 端预计算并传递给 Shader,避免在 GPU 上进行昂贵的矩阵求逆运算。

矩阵	作用	输入空间	输出空间
View (V)	将世界坐标转换到相机视角	World Space	View Space
Projection (P)	将视图坐标投影到裁剪空间	View Space	Clip Space
VP	组合变换,一步到位	World Space	Clip Space
(VP)⁻¹	逆向重建世界坐标	NDC Space	World Space
四、应用场景
深度重建是众多屏幕空间技术的基石。掌握这项技术后,你可以实现以下效果:

SSAO
Screen Space Ambient Occlusion
屏幕空间环境光遮蔽

SSR
Screen Space Reflections
屏幕空间反射

体积雾
Volumetric Fog
基于深度的雾效计算

SSAO 实现要点
SSAO.frag (片段着色器)
// 采样周围多个点进行深度比较
float CalculateAO(float2 uv, float3 normal)
{
    float ao = 0.0;
    
    // 获取当前像素的世界坐标
    float depth = SAMPLE_TEXTURE2D(_CameraDepthTexture, uv);
    float3 currentPos = ReconstructWorldPosition(uv, depth);
    
    // 在半球方向采样多个点
    for (int i = 0; i < 16; i++)
    {
        float3 sampleDir = GetHemisphereSample(i, normal);
        float3 samplePos = currentPos + sampleDir * radius;
        
        // 将采样点投影回屏幕空间
        float2 sampleUV = ProjectToScreen(samplePos);
        float sampleDepth = SAMPLE_TEXTURE2D(_CameraDepthTexture, sampleUV);
        
        // 深度差异决定遮蔽程度
        float rangeCheck = smoothstep(0.0, 1.0, radius / abs(currentPos.z - samplePos.z));
        ao += (samplePos.z < currentPos.z ? 1.0 : 0.0) * rangeCheck;
    }
    
    return 1.0 - (ao / 16.0);
}

五、关键要点总结

NDC 坐标转换

uv * 2.0 - 1.0 是将 [0,1] 范围映射到 [-1,1] 的标准操作

透视除法不可省略

w 分量保存了原始深度信息,必须除以 w 才能得到正确的世界坐标

逆矩阵计算时机

建议在 C# 端计算逆矩阵并通过 SetMatrix 传递,避免 GPU 端的矩阵求逆开销

深度纹理格式

确保在 URP Asset 中开启 Depth Texture,否则 m_DepthTexture 将为 null

SRP Batcher 兼容性

使用常量缓冲区传递矩阵时,需确保格式兼容以获得最佳性能

六、URP 配置要点

要在 URP 中使用深度重建功能,需要正确配置渲染管线资产:

1

打开 URP Asset

选中你的 URP Renderer Asset

2

启用 Depth Texture

勾选 "Depth Texture" 选项

代码中启用深度纹理(备选方案):

cs 复制代码
// 在你的 Renderer 的 BeginCameraRendering 中
var cameraData = renderingData.cameraData;
if (cameraData.cameraType == CameraType.Game)
{
    cameraData.camera.depthTextureMode |= DepthTextureMode.Depth;
}
相关推荐
mxwin6 小时前
unity shader中 ddx ddy是什么
unity·游戏引擎·shader
郝学胜-神的一滴9 小时前
[简化版 GAMES 101] 计算机图形学 08:三角形光栅化上
c++·unity·游戏引擎·godot·图形渲染·opengl·unreal
nnsix9 小时前
Unity ILRuntime 笔记
unity·游戏引擎
nnsix11 小时前
Unity API 兼容的 .NET Standard 2.1 和 .NET Framework 区别
unity·游戏引擎·.net
mxwin11 小时前
Unity Shader 制作半透明物体 使用多Pass提前写入深度的方式 避免穿模
unity·游戏引擎
nnsix13 小时前
Unity HybridCLR 笔记
笔记·unity·游戏引擎
nnsix14 小时前
Unity Addressables 笔记
unity·游戏引擎
RReality14 小时前
【Unity Shader URP】视差贴图 实战教程
ui·平面·unity·游戏引擎·图形渲染·贴图
小清兔1 天前
Addressable的设置打包流程
笔记·游戏·unity·c#
3D霸霸1 天前
Sourcetree 拉取新工程
数据仓库·unity