当前位置: 首页 > news >正文

Unity Shader 深度重建世界坐标

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

一、核心原理

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

Pworld = (VP)-1 × Pndc

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

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

二、完整实现代码

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

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); } }
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进行深度重建 } }
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 SpaceView Space
Projection (P)将视图坐标投影到裁剪空间View SpaceClip Space
VP组合变换,一步到位World SpaceClip Space
(VP)⁻¹逆向重建世界坐标NDC SpaceWorld Space

四、应用场景

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

SSAO

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

SSR

Screen Space Reflections
屏幕空间反射

体积雾

Volumetric Fog
基于深度的雾效计算

SSAO 实现要点

性能提示:逆 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" 选项

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

// 在你的 Renderer 的 BeginCameraRendering 中 var cameraData = renderingData.cameraData; if (cameraData.cameraType == CameraType.Game) { cameraData.camera.depthTextureMode |= DepthTextureMode.Depth; }
http://www.jsqmd.com/news/675597/

相关文章:

  • 2026年质量好的燃气型热风旋转炉/64盘推车式热风旋转炉/16盘推车式热风旋转炉实力工厂怎么选 - 品牌宣传支持者
  • Cadence 17.4 新手避坑指南:从Orcad原理图到Allegro PCB的完整流程(附常见报错解决)
  • 如何快速掌握Dev-CPP:5个高效使用秘诀与完整指南
  • Parseable Kafka连接器深度解析:实现实时数据流处理
  • 告别抢票焦虑:B站会员购自动化工具biliTickerBuy完全指南
  • 2026年评价高的广州军事化夏令营/广州军事夏令营/军事化夏令营人气推荐 - 行业平台推荐
  • TranslucentTB透明任务栏终极教程:让你的Windows桌面焕然一新
  • 2026年质量好的家用烤箱低糖空气炸款/家用烤箱搪瓷内胆款/家用烤箱小型迷你款厂家选择指南 - 行业平台推荐
  • Moody’s Agentic Solutions登陆AWS Marketplace
  • 3步解锁QQ音乐加密文件:qmcdump终极解密指南
  • 2026年质量好的研学冬令营/海南小学生冬令营/黄埔军校冬令营/广州冬令营热门推荐 - 品牌宣传支持者
  • AccessControl.js未来展望:探索下一代权限控制技术趋势
  • 内容运营自动化AI工具选型指南:2026企业级智能内容工程实践全解析
  • MVAA 2026 二尖瓣多模态解剖分析挑战赛全面启动!
  • 2026年靠谱的PVC塑料管材设备/pert塑料管材设备/塑料管材设备制造厂家哪家靠谱 - 品牌宣传支持者
  • NVIDIA Profile Inspector终极指南:解锁隐藏驱动设置,优化游戏性能
  • 2026年论文降AI必备指南:三分钟亲测AIGC率从80%降到10% - 降AI实验室
  • 【matlab代码】轨迹漂移时,利用终点位置的轨迹校正,matlab例程,可用于降低惯导漂移带来的误差,适用于三维空间|附完整代码
  • SpringBoot+Vue办公用品管理系统源码+论文
  • 深入理解DSP28335的PWM模块:如何用EPWM实现三相电机控制(附代码分析)
  • 2026年靠谱的塑料渗水管波纹管设备/pe波纹管设备厂家综合实力对比 - 行业平台推荐
  • 保姆级教程:在Ubuntu 22.04上为Zabbix Server 5.0.3配置被动模式Agent
  • 哔哩下载姬DownKyi:3步轻松免费下载B站8K高清视频的终极方案
  • 如何用5个技巧高效采集小红书内容?XHS-Downloader实战指南
  • 深聊水泥罐选购,探讨靠谱的水泥罐定制供应商哪家性价比高 - 工业设备
  • DLSS Swapper深度解析:多平台游戏渲染技术版本管理架构揭秘
  • Tsuru平台缓存策略终极指南:10个提升应用性能的关键技巧
  • Arduino串口通讯实战:从Serial.begin到Serial.println的完整指南(附按钮状态监测案例)
  • NVIDIA Profile Inspector终极指南:解锁显卡隐藏性能的10个技巧
  • 哔哩下载姬DownKyi终极指南:如何快速掌握B站视频下载技巧