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

Unity Shader 深入理解 LinearEyeDepth 与 DepthTexture

从透视投影的数学本质出发,拆解深度缓冲区的非线性编码原理,掌握 LinearEyeDepth 的完整变换链路与实战技巧。

一、Camera DepthTexture:它是什么

在 Unity 中,启用DepthTextureMode.Depth后,渲染管线会在不透明物体绘制完毕后、透明物体绘制之前,额外生成一张全屏的深度纹理——这就是_CameraDepthTexture。它本质上是将当前帧的深度缓冲区(Depth Buffer)内容拷贝到一张可被 Shader 采样的纹理中。

💡 关键要点

半透明物体的 Shader 可以直接采样 _CameraDepthTexture——这是水面、玻璃等效果的基础。URP 中不透明物体队列为RenderQueue ≤ 2500,因此任何在 Transparent 队列(3000)渲染的材质都能读到底层场景的深度。

二、为什么深度是非线性的

GPU 写入深度缓冲区的值并不是世界空间中的真实距离,而是经过透视投影矩阵变换后的非线性值。理解这一点,是掌握LinearEyeDepth的前提。

2.1 透视投影的本质

透视投影将视锥体(Frustum)映射到 NDC 立方体[-1, 1]3。在这个变换中,z 分量经历了非线性压缩:近平面附近的深度精度极高,远平面附近的精度则急剧下降。

2.2 深度缓冲区中的值

采样_CameraDepthTexture得到的是一个0 到 1 的浮点数,它遵循以下公式(OpenGL / Unity 约定):

Depthbuffer = ( far · (z − near) ) / ( z · (far − near) )

其中z是观察空间中的深度(相机前方的实际距离),nearfar是裁剪面距离。这个曲线在近平面附近陡峭,在远平面附近平坦——这就是 z-fighting 更常出现在远处的根本原因

三、LinearEyeDepth:从非线性到线性

3.1 它做了什么

LinearEyeDepth是 URP 中Common.hlsl提供的核心函数,它将采样到的非线性深度值还原为观察空间中的线性距离。这意味着转换后的值可以直接用于计算世界空间位置、做距离比较、驱动雾效等。

// URP / Core RP Library 中的实现 float LinearEyeDepth(float rawDepth, float4 zBufferParam) { return 1.0 / (zBufferParam.x * rawDepth + zBufferParam.y); }

其中zBufferParam由引擎在每帧计算:

zBufferParam.x = (far − near) / far
zBufferParam.y = near / far

3.2 数学推导

将原始非线性公式取倒数即可还原:

原始:rawDepth = far · (z − near) / (z · (far − near))

取倒数:1 / rawDepth = z · (far − near) / (far · (z − near))

整理后得到:z = 1 / ( ((far−near)/far) · rawDepth + near/far )

这就是LinearEyeDepth的完整数学本质——一个有理函数的倒数

📐 与 Linear01Depth 的区别

Linear01Depth(rawDepth, zBufferParam)返回的是[0, 1] 归一化深度(0 = near, 1 = far),适合做深度比较。

LinearEyeDepth(rawDepth, zBufferParam)返回的是观察空间的实际距离(米),适合做世界空间重建或距离相关的计算。

四、如何正确采样深度纹理

4.1 声明纹理与采样器

// URP 中推荐使用宏声明(自动处理平台兼容性) TEXTURE2D_FLOAT(_CameraDepthTexture); SAMPLER(sampler_CameraDepthTexture);

4.2 在 Shader 中采样与转换

// 1. 计算屏幕空间 UV float4 screenPos = ComputeScreenPos(positionCS); float2 screenUV = screenPos.xy / screenPos.w; // 透视除法 // 2. 采样深度纹理(R 通道即为深度值) float rawDepth = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, sampler_CameraDepthTexture, screenUV); // 3. 转换为观察空间线性深度 float eyeDepth = LinearEyeDepth(rawDepth, _ZBufferParams); // 4. (可选)转换为 01 深度 float linear01 = Linear01Depth(rawDepth, _ZBufferParams);

4.3 关键注意事项

⚠️ 常见陷阱

  • 记得透视除法:ComputeScreenPos返回的是齐次坐标,必须除以.w才是正确的 UV。
  • Vulkan / Metal 平台:这些 API 下深度值已经在线性空间中(如果启用了 Reverse-Z),_ZBufferParams会自动适配。
  • Scene Depth vs Camera Depth:_CameraDepthTexture不包含透明物体;若需包含透明物体深度,使用_CameraDepthAttachment(URP 14+)。
  • 深度纹理精度:大部分移动平台为 16位或24位深度格式,远处精度有限,避免在远平面附近做高精度深度比较。

五、实战场景分析

5.1 场景一:水面边缘的软过渡(深度差)

水面 Shader 的经典写法:比较水面像素深度与场景深度,在物体与水面交界处产生泡沫或边缘效果。

float sceneEyeDepth = LinearEyeDepth( SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, sampler_CameraDepthTexture, screenUV), _ZBufferParams ); float waterEyeDepth = screenPos.w; // 水面像素在观察空间的深度 float depthDiff = sceneEyeDepth - waterEyeDepth; // 深度差越小 = 物体越靠近水面 = 泡沫强度越高 float foamFactor = saturate(1.0 - depthDiff / _FoamDistance);

5.2 场景二:从深度重建世界空间坐标

结合LinearEyeDepth与 NDC 坐标,可以精确重建每个像素的世界位置——这是屏幕空间效果(SSR、SSAO、贴花)的核心技术。

float3 ReconstructWorldPos(float2 screenUV, float rawDepth) { // 1. 得到观察空间线性深度 float eyeDepth = LinearEyeDepth(rawDepth, _ZBufferParams); // 2. 构建 NDC 坐标(xy 映射到 [-1,1]) float3 ndcPos = float3(screenUV * 2.0 - 1.0, 1.0); // 3. NDC → 观察空间(乘以深度) float3 viewPos = mul(unity_CameraInvProjection, float4(ndcPos, 1.0)).xyz * eyeDepth; // 4. 观察空间 → 世界空间 float3 worldPos = mul(unity_CameraToWorld, float4(viewPos, 1.0)).xyz; return worldPos; }

🔧 ASE 节点映射

如果你使用 Amplify Shader Editor,以上操作可以通过以下节点实现:
Depth TextureEye Depth节点(自动调用 LinearEyeDepth)→ 配合Screen PositionInverse View Projection Matrix重建世界坐标。

5.3 场景三:水下角色渲染修正

这是一个经典的深度排序问题:当角色半身浸入水中时,水面 Shader 需要区分"水面到场景"和"水面到角色"的深度差,避免在不正确的深度层产生扭曲。

关键修正逻辑——在深度比较时取min,确保角色后方物体不会干扰水面的折射计算:

// 采样场景不透明深度 float rawSceneDepth = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, sampler_CameraDepthTexture, screenUV); float sceneEyeDepth = LinearEyeDepth(rawSceneDepth, _ZBufferParams); // 水面像素自身的观察空间深度 float waterEyeDepth = screenPos.w; // 关键修正:取水面和场景深度中的较近者 // 避免角色背后的冰块等物体产生错误扭曲 float effectiveSceneDepth = min(sceneEyeDepth, waterEyeDepth); // 正确的水面-场景深度差 float correctedDiff = effectiveSceneDepth - waterEyeDepth; // 若差值很小或为负 → 有物体在水面附近或上方 → 减少扭曲 float distortionMask = saturate(correctedDiff / _MaxDistortionDepth);

六、函数速查表

函数 / 宏输入输出用途
SAMPLE_DEPTH_TEXTURE纹理 + UVraw depth [0,1]从 _CameraDepthTexture 采样
LinearEyeDepthrawDepth + _ZBufferParams观察空间距离(米)世界坐标重建、真实距离计算
Linear01DepthrawDepth + _ZBufferParams[0,1] 线性深度深度比较、软粒子、雾效
ComputeScreenPospositionCS齐次屏幕坐标计算采样 UV(需除 .w)
_ZBufferParamsfloat4 (x,y,z,w)URP 自动传入的深度反算参数

七、小结

LinearEyeDepth的代码虽然只有一行,但它背后承载的是透视投影的完整数学链路:

  1. 深度缓冲区存储的是非线性值——近处精度高、远处精度低,这是透视投影矩阵的必然结果。
  2. LinearEyeDepth用一个有理函数的倒数将非线性深度还原为观察空间线性距离。
  3. _ZBufferParams由引擎根据near/far自动计算,适配不同图形 API(OpenGL / D3D / Vulkan / Metal)的深度范围约定。
  4. 实战中最常见的错误——忘记透视除法、没处理 Reverse-Z 平台差异、角色背后物体干扰深度比较——都可以通过理解上述原理来避免。

📚 延伸阅读

如果你想进一步深入,推荐阅读 URP 源码中的Common.hlslDepthOnlyPass.hlsl,以及 Unity 官方的Frame Debugger来验证每个 Pass 的深度写入行为。配合 ASE 的Eye Depth节点,你可以不写一行代码就完成大部分深度相关效果。

http://www.rkmt.cn/news/1530225.html

相关文章:

  • NOIP2010普及组「接水问题」详解:模拟算法与优先队列解法
  • 构建智能视频嗅探缓存系统:VBrowser-Android技术深度解析
  • 深入解析Cimoc漫画阅读器:多源聚合架构与高效渲染技术实战
  • PowerPC e300中断机制深度解析:从DSI到SMI的实战指南
  • 申论写作‘避坑指南’:从阅卷视角拆解大作文的4个致命失分点(附修改对比)
  • 一个小失误,差点怀疑人生
  • 深入解析MSC711x统一内存映射:从总线架构到嵌入式驱动实践
  • 得得美家:装修全包企业,深耕北京地区,打造值得信赖的品质放心家装 - 十大品牌榜
  • 如何在Windows电脑上轻松安装APK文件:APK-Installer完全指南
  • 终极指南:免费获取B站直播推流码,告别官方直播姬限制 [特殊字符]
  • Excel转PDF保姆级指南:2026年最全4种官方方法手把手教你
  • 得得美家:装修半包企业,凭借多年的行业沉淀与务实的服务理念深耕北京,省心靠谱之选 - 十大品牌榜
  • FLUX.1-dev模型量化技术深度解析:从bnb-nf4到V2版本的演进与实践指南
  • 三一-西门子AI数字化资深顾问钱士明|长沙站《AI赋能制造业高质量数字化》
  • Little Navmap:飞行规划工具的三层架构设计与性能优化深度解析
  • 从隐藏性能到极致释放:Universal-x86-Tuning-Utility 硬件调优完全指南
  • DLSS Swapper实战指南:3步解锁NVIDIA显卡隐藏性能的完全解决方案
  • 夜光不亮了?别自己涂!2026亨得利深圳手表夜光失效修复全记录:劳力士/欧米茄/百达翡丽实测,原厂夜光粉填充与避坑指南 - 亨得利腕表维修中心
  • 基因组结构方程建模终极指南:如何用GenomicSEM破解多性状遗传分析难题
  • 什么是OEE?终于有人把OEE彻底说清了!
  • 避坑指南:用MATLAB处理海洋nc数据时,你可能遇到的5个报错及解决方法
  • 调查研究-178 Google 官方 Agent Skills 仓库解读:AI Agent 时代,知识正在从「提示词」变成「可安装能力包」
  • 别再被网站识破了!用Chromedp + Go 实现‘隐身’爬虫的完整配置清单
  • 2026洗发水贴牌代工全攻略:资质、研发、品控、起订量,一次讲透 - 品研笔录
  • 重庆餐饮家具工厂怎么选?5 家正规源头品牌深度实测推荐 - kio888
  • 【提升办公效率】 小龙虾 OpenClaw 全流程安装与功能使用讲解(含安装包)
  • 视频去水印工具推荐:2026免费本地软件与App实测
  • 终极指南:WaveTools鸣潮工具箱抽卡记录数据同步异常排查与修复
  • 深入解析ColdFire2/2M总线协议:从信号到时序的嵌入式硬件设计指南
  • 告别模拟器配置噩梦:EmuDeck一键打造你的Steam Deck怀旧游戏库