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;
}
相关推荐
WarPigs14 小时前
游戏签到系统
unity
小拉达不是臭老鼠17 小时前
Unity中的UI系统之UGUI
学习·ui·unity
万兴丶17 小时前
Coplay适用于 Unity 的“Al 代理”使用指南
unity·游戏引擎·ai编程
魔士于安20 小时前
Unity材质球大合集
unity·游戏引擎·材质
mxwin1 天前
Unity Shader 冰面 Shader 制作原理与流程
unity·游戏引擎·shader
玖玥拾1 天前
Cocos学习笔记:关卡系统、音频管理与物理控制
游戏引擎·cocos2d
小拉达不是臭老鼠1 天前
Unity中的UI系统之UGUI_登陆面板实现
ui·unity
郝学胜-神的一滴1 天前
[简化版 GAMES 101] 计算机图形学 11:频域·卷积·抗锯齿
c++·unity·图形渲染·opengl·three·unreal
玖玥拾1 天前
Cocos学习笔记:滚动视图、关卡系统与本地存储
游戏引擎·cocos2d
元气少女小圆丶2 天前
SenseGlove Nova 2+Unity开发笔记2
笔记·unity·游戏引擎