UnityShader Linear01Depth和LinearEyeDepth函数
Unity ShaderLinear01Depth 和 LinearEyeDepth在屏幕后处理、水面交界、软粒子、雾效和 SSAO 里经常需要读取_CameraDepthTexture。采样出来的值一般不能直接当作场景距离使用因为透视投影写入 Z Buffer 的深度不是线性分布的。Unity Built-in Render Pipeline 提供了两个常用函数Linear01Depth把原始深度转换为相对远裁剪面的线性深度LinearEyeDepth把原始深度转换为观察空间中的正向深度。本文对照 Unity 2022.3.62f3 的UnityCG.cginc和UnityShaderVariables.cginc重点说明这两个函数的输入、输出和使用边界。一、先分清几种深度讨论函数之前先把几个容易混淆的概念分开。1. Raw Depth从深度纹理采样得到的原始硬件深度float rawDepth SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, uv);透视相机下这个值通常位于 0 到 1但它不是线性距离。Raw Depth 等于 0.5并不表示物体位于 Near 和 Far 的正中间。普通 Z Buffer 中Near → 0 Far → 1Reversed-Z 中正好相反Near → 1 Far → 02. Linear01 DepthLinear01Depth的结果等于观察空间深度除以远裁剪面Linear01Depth EyeDepth f a r \text{Linear01Depth}\frac{\text{EyeDepth}}{far}Linear01DepthfarEyeDepth​所以它的实际范围是Near → near / far Far → 1Near 并不严格等于 0。只是大多数相机的near / far很小看起来接近 0。3. Linear Eye DepthLinearEyeDepth返回观察空间 View Z 方向上的正数深度Near → near Far → far如果 Unity 场景中一个单位表示一米那么返回 10 可以理解为物体沿相机观察轴方向约 10 米深。它不是相机到该像素位置的欧氏距离LinearEyeDepth(rawDepth) ! distance(_WorldSpaceCameraPos, worldPos);画面边缘的点尤其要注意这个区别。二、Unity 官方函数Unity 2022.3.62f3 的UnityCG.cginc中定义为// Z buffer to linear 0..1 depth inline float Linear01Depth(float z) { return 1.0 / (_ZBufferParams.x * z _ZBufferParams.y); } // Z buffer to linear depth inline float LinearEyeDepth(float z) { return 1.0 / (_ZBufferParams.z * z _ZBufferParams.w); }这里的z应该是从深度纹理采样得到的 Raw Depth不是 Clip Space Z、View Space Z也不是已经线性化过的深度。三、_ZBufferParams的含义Unity 会根据相机的 Near、Far 和当前图形 API 自动填写_ZBufferParams。设n near f far1. 普通 Z Buffer分量数值_ZBufferParams.x1 − f n 1-\frac{f}{n}1−nf​_ZBufferParams.yf n \frac{f}{n}nf​_ZBufferParams.zx f 1 − f n f 1 f − 1 n \frac{x}{f}\frac{1-\frac{f}{n}}{f}\frac{1}{f}-\frac{1}{n}fx​f1−nf​​f1​−n1​_ZBufferParams.wy f f n f 1 n \frac{y}{f}\frac{\frac{f}{n}}{f}\frac{1}{n}fy​fnf​​n1​2. Reversed-Z在 D3D11、Metal、Vulkan 等常见平台上Unity 通常使用 Reversed-Z分量数值_ZBufferParams.x− 1 f n -1\frac{f}{n}−1nf​_ZBufferParams.y1 11_ZBufferParams.zx f − 1 f n f 1 n − 1 f \frac{x}{f}\frac{-1\frac{f}{n}}{f}\frac{1}{n}-\frac{1}{f}fx​f−1nf​​n1​−f1​_ZBufferParams.wy f 1 f \frac{y}{f}\frac{1}{f}fy​f1​官网写的z x / far和w y / far中x、y指的是同一个_ZBufferParams当前已经确定的 x、y 分量。开启 Reversed-Z 后先把 x、y 换成 Reversed-Z 的值再计算 z、w并不是继续沿用普通 Z 的 x、y。Unity 已经把普通 Z 和 Reversed-Z 的差异编码进_ZBufferParams所以把正确采样到的 Raw Depth 直接传给这两个函数即可。不要在调用前又手动执行一次1 - rawDepth否则会重复反转。官方变量说明https://docs.unity3d.com/2022.3/Documentation/Manual/SL-UnityShaderVariables.html四、Linear01Depth 的推导官方实现inline float Linear01Depth(float z) { return 1.0 / (_ZBufferParams.x * z _ZBufferParams.y); }先看普通 Z Buffer_ZBufferParams.x 1 - far / near _ZBufferParams.y far / near代入可得Linear01Depth ( z ) 1 ( 1 − f a r n e a r ) z f a r n e a r \text{Linear01Depth}(z)\frac{1}{\left(1-\frac{far}{near}\right)z\frac{far}{near}}Linear01Depth(z)(1−nearfar​)znearfar​1​1. Near Plane普通 Z Buffer 中Near 对应z 0Linear01Depth ( 0 ) 1 f a r / n e a r n e a r f a r \text{Linear01Depth}(0)\frac{1}{far/near}\frac{near}{far}Linear01Depth(0)far/near1​farnear​2. Far Plane普通 Z Buffer 中Far 对应z 1Linear01Depth ( 1 ) 1 1 − f a r / n e a r f a r / n e a r 1 \text{Linear01Depth}(1)\frac{1}{1-far/nearfar/near}1Linear01Depth(1)1−far/nearfar/near1​1因此它不是严格的[0, 1]映射而是[ n e a r f a r , 1 ] \left[\frac{near}{far},1\right][farnear​,1]3. 如果需要 Near 严格等于 0可以先得到 Eye Depth再重新归一化float eyeDepth LinearEyeDepth(rawDepth); float strictDepth01 saturate( (eyeDepth - _ProjectionParams.y) / (_ProjectionParams.z - _ProjectionParams.y) );其中_ProjectionParams.y near _ProjectionParams.z far五、LinearEyeDepth 的推导官方实现inline float LinearEyeDepth(float z) { return 1.0 / (_ZBufferParams.z * z _ZBufferParams.w); }普通 Z Buffer 中_ZBufferParams.z 1 / far - 1 / near _ZBufferParams.w 1 / near代入可得LinearEyeDepth ( z ) 1 ( 1 f a r − 1 n e a r ) z 1 n e a r \text{LinearEyeDepth}(z)\frac{1}{\left(\frac{1}{far}-\frac{1}{near}\right)z\frac{1}{near}}LinearEyeDepth(z)(far1​−near1​)znear1​1​整理后LinearEyeDepth ( z ) 1 z f a r 1 − z n e a r \text{LinearEyeDepth}(z)\frac{1}{\frac{z}{far}\frac{1-z}{near}}LinearEyeDepth(z)farz​near1−z​1​普通 Z Buffer 的两个边界为LinearEyeDepth ( 0 ) n e a r \text{LinearEyeDepth}(0)nearLinearEyeDepth(0)nearLinearEyeDepth ( 1 ) f a r \text{LinearEyeDepth}(1)farLinearEyeDepth(1)far这里返回的是观察空间中的正向深度不是“世界空间深度”。Unity 相机观察空间通常朝-Z而该函数返回的是正数深度大小。六、两个函数之间的关系因为_ZBufferParams.z _ZBufferParams.x / far _ZBufferParams.w _ZBufferParams.y / far所以LinearEyeDepth ( z ) f a r ⋅ Linear01Depth ( z ) \text{LinearEyeDepth}(z)far\cdot\text{Linear01Depth}(z)LinearEyeDepth(z)far⋅Linear01Depth(z)反过来Linear01Depth ( z ) LinearEyeDepth ( z ) f a r \text{Linear01Depth}(z)\frac{\text{LinearEyeDepth}(z)}{far}Linear01Depth(z)farLinearEyeDepth(z)​这也解释了为什么Linear01Depth在 Near Plane 返回的是near / far而不是严格的 0。七、数值例子假设相机参数为near 0.3 far 1000普通 Z Buffer 下_ZBufferParams.x ≈ -3332.333333 _ZBufferParams.y ≈ 3333.333333 _ZBufferParams.z ≈ -3.332333 _ZBufferParams.w ≈ 3.333333计算结果Raw DepthLinear01DepthLinearEyeDepth00.00030.30.5约 0.00059982约 0.59982111000Raw Depth 为 0.5 时Eye Depth 只有约 0.6而不是 500。这正是透视深度非线性的表现大量深度精度集中在相机附近。在 Reversed-Z 下Near 和 Far 对应的 Raw Depth 会交换位置Raw DepthLinear01DepthLinearEyeDepthNear10.00030.3Far011000输出结果没有反过来因为_ZBufferParams已经同步变化。八、为什么透视深度不是线性的透视投影需要让远处物体看起来更小。完成透视除法后写入深度缓冲的值大致与1 / EyeDepth相关而不是与 Eye Depth 成正比。这样做会把更多深度精度留给 Near Plane 附近相机附近Raw Depth 变化快精度高 远处区域Raw Depth 逐渐挤在一起精度低Near 设置得过小会浪费大量深度精度也更容易出现 Z-Fighting。Reversed-Z 配合浮点深度缓冲可以改善远距离精度但它不会让 Raw Depth 变成线性值。九、正交相机需要单独处理前面的倒数公式是按透视投影推导的。正交相机没有透视除法Raw Depth 本身就是线性变化的不应直接套用透视相机的解释。可以使用unity_OrthoParams.w判断相机是否为正交相机unity_OrthoParams.w 1正交相机 unity_OrthoParams.w 0透视相机正交相机的 Eye Depth 可以这样计算float OrthographicEyeDepth(float rawDepth) { #if defined(UNITY_REVERSED_Z) rawDepth 1.0 - rawDepth; #endif return lerp( _ProjectionParams.y, _ProjectionParams.z, rawDepth ); }同时支持两种相机时可以分开处理float GetEyeDepth(float rawDepth) { if (unity_OrthoParams.w 0.5) { return OrthographicEyeDepth(rawDepth); } return LinearEyeDepth(rawDepth); }十、Built-in 中的深度采样示例下面给出顶点到片元阶段的核心代码。顶点阶段用ComputeScreenPos生成屏幕坐标片元阶段用投影采样宏读取深度#include UnityCG.cginc UNITY_DECLARE_DEPTH_TEXTURE(_CameraDepthTexture); struct v2f { float4 pos : SV_POSITION; float4 screenPos : TEXCOORD0; }; v2f vert(appdata_base v) { v2f o; o.pos UnityObjectToClipPos(v.vertex); o.screenPos ComputeScreenPos(o.pos); return o; } float4 frag(v2f i) : SV_Target { float rawDepth SAMPLE_DEPTH_TEXTURE_PROJ( _CameraDepthTexture, UNITY_PROJ_COORD(i.screenPos) ); float eyeDepth; if (unity_OrthoParams.w 0.5) { float orthoDepth rawDepth; #if defined(UNITY_REVERSED_Z) orthoDepth 1.0 - orthoDepth; #endif eyeDepth lerp( _ProjectionParams.y, _ProjectionParams.z, orthoDepth ); } else { eyeDepth LinearEyeDepth(rawDepth); } float strictDepth01 saturate( (eyeDepth - _ProjectionParams.y) / (_ProjectionParams.z - _ProjectionParams.y) ); return float4( strictDepth01, strictDepth01, strictDepth01, 1 ); }相机需要生成深度纹理CameracameraGetComponentCamera();camera.depthTextureMode|DepthTextureMode.Depth;Built-in 的_CameraDepthTexture通常只包含 Render Queue 小于或等于 2500 的不透明物体。物体使用的 Shader 还需要存在可用的ShadowCasterPass 或能从 Fallback 中取得该 Pass否则它可能不会写入相机深度纹理。十一、常见错误1. 把 Linear01Depth 当成严格的 0 到 1它实际是Near → near / far Far → 1只有在near / far很小时Near 才近似为 0。2. 把 LinearEyeDepth 写成世界空间距离它是沿 View Z 方向的深度不是distance(_WorldSpaceCameraPos, worldPos)3. 对已经线性化的深度再次线性化下面这种写法是错误的float eyeDepth LinearEyeDepth(rawDepth); float wrong LinearEyeDepth(eyeDepth);4. 认为 Raw Depth 为 0.5 就是距离中点透视相机下Raw Depth 和实际距离不是线性关系。5. 手动反转后又调用 Unity 函数对于透视相机Unity 已通过_ZBufferParams处理 Reversed-Z。不要先执行rawDepth 1.0 - rawDepth;再调用Linear01Depth或LinearEyeDepth。6. 忽略正交相机正交相机的深度本身是线性的应按 Near 和 Far 插值。7. 忘记开启深度纹理Built-in 相机需要启用camera.depthTextureMode|DepthTextureMode.Depth;8. 把 Built-in 代码直接复制到 URP/HDRPURP/HDRP 的深度纹理声明、采样和平台处理接口不同。URP 通常使用SampleSceneDepth等 SRP 函数不应直接照搬 Built-in 的完整代码。十二、这两个函数分别适合什么场景Linear01Depth适合只关心相对深度比例的效果深度可视化按远裁剪面归一化的雾效简单的深度渐变调试深度纹理。LinearEyeDepth适合需要场景单位的效果水面和物体交界处的 Depth FadeSoft Particles按米计算的雾效屏幕空间位置重建SSAO、SSR 等屏幕空间算法。十三、总结Raw Depth 是硬件深度透视相机下不是线性的Linear01Depth返回EyeDepth / far范围为[near / far, 1]LinearEyeDepth返回观察空间 View Z 方向上的正数深度LinearEyeDepth不是相机到像素的欧氏距离Unity 通过_ZBufferParams自动适配普通 Z 和 Reversed-Z透视相机不要手动反转 Raw Depth 后再调用这两个函数正交相机需要按 Near、Far 单独线性插值Built-in、URP 和 HDRP 的深度采样接口不能直接混用。

相关新闻

技术探秘:Windows 10上实现Android应用生态桥接的完整指南

技术探秘:Windows 10上实现Android应用生态桥接的完整指南

技术探秘:Windows 10上实现Android应用生态桥接的完整指南 【免费下载链接】WSA-Windows-10 This is a backport of Windows Subsystem for Android to Windows 10. 项目地址: https://gitcode.com/gh_mirrors/ws/WSA-Windows-10 在Windows 10平台上实现And…

2026/7/31 18:42:21 阅读更多 →
3小时从零到精通:Reloaded-II游戏模组加载器终极指南

3小时从零到精通:Reloaded-II游戏模组加载器终极指南

3小时从零到精通:Reloaded-II游戏模组加载器终极指南 【免费下载链接】Reloaded-II Universal .NET Core Powered Modding Framework for any Native Game X86, X64. 项目地址: https://gitcode.com/gh_mirrors/re/Reloaded-II 你是否渴望为心爱的游戏添加全…

2026/7/31 18:42:21 阅读更多 →
大疆 M30T H20T H30T 御2行业进阶版(M2EA) 热红外图像jpg转tiff或tif,并将tiff或tif使用pix4d合成 | 热红外照片温度信息提取(方法三)

大疆 M30T H20T H30T 御2行业进阶版(M2EA) 热红外图像jpg转tiff或tif,并将tiff或tif使用pix4d合成 | 热红外照片温度信息提取(方法三)

看到很多人不会处理大疆无人机拍摄的热红外照片,也不怎么会合成正射影像,在这里我介绍一种处理大疆多种无人机热红外成像照片的方法,以及将处理好的热红外照片拼接成正射影像(大图)。由于大疆R-JPEG格式照片无法直接导…

2026/7/31 18:42:21 阅读更多 →

最新新闻

掌握Flutter Picker联动选择器:ArrayPicker实现多级联动效果

掌握Flutter Picker联动选择器:ArrayPicker实现多级联动效果

掌握Flutter Picker联动选择器:ArrayPicker实现多级联动效果 【免费下载链接】flutter_picker Flutter picker plugin 项目地址: https://gitcode.com/gh_mirrors/fl/flutter_picker Flutter Picker是一款功能强大的Flutter选择器插件,能够帮助开…

2026/7/31 19:24:06 阅读更多 →
告别设计焦虑!Guizang Social Card Skill:零基础制作杂志级社交媒体封面图的终极指南

告别设计焦虑!Guizang Social Card Skill:零基础制作杂志级社交媒体封面图的终极指南

告别设计焦虑!Guizang Social Card Skill:零基础制作杂志级社交媒体封面图的终极指南 【免费下载链接】guizang-social-card-skill 🪧 Claude Code / Codex skill — generate Xiaohongshu carousels & WeChat 21:91:1 cover pairs. Edit…

2026/7/31 19:24:06 阅读更多 →
Flutter Picker高级技巧:自定义样式与灵活配置参数详解

Flutter Picker高级技巧:自定义样式与灵活配置参数详解

Flutter Picker高级技巧:自定义样式与灵活配置参数详解 【免费下载链接】flutter_picker Flutter picker plugin 项目地址: https://gitcode.com/gh_mirrors/fl/flutter_picker Flutter Picker是一款功能强大的Flutter选择器插件,帮助开发者轻松实…

2026/7/31 19:24:06 阅读更多 →
LNMP+Redis全栈部署实战:Nginx+MySQL+PHP+Redis一站通

LNMP+Redis全栈部署实战:Nginx+MySQL+PHP+Redis一站通

Redis 与 LNMP 集成功能概述 Redis 核心功能 内存数据存储:高速读写性能 数据结构丰富:字符串、哈希、列表、集合等 持久化支持:RDB快照和AOF日志 发布订阅:消息队列功能 高可用:主从复制、哨兵模式、集群 LNMP+Redis 集成价值 会话共享:多Web服务器共享Session 数据缓存…

2026/7/31 19:24:06 阅读更多 →
TypeScript友好的进度条库:BProgress类型定义与类型安全实践

TypeScript友好的进度条库:BProgress类型定义与类型安全实践

TypeScript友好的进度条库:BProgress类型定义与类型安全实践 【免费下载链接】next-nprogress-bar BProgress is a lightweight, customizable progress bar for better user experience. 项目地址: https://gitcode.com/gh_mirrors/ne/next-nprogress-bar B…

2026/7/31 19:24:06 阅读更多 →
Codex 生成代码不等于交付:用测试和审查识别 AI 编程幻觉

Codex 生成代码不等于交付:用测试和审查识别 AI 编程幻觉

Codex 生成代码不等于交付:用测试和审查识别 AI 编程幻觉 OK,OK,大家好,欢迎大家来到大鹏 AI 教育,我是张大鹏。 AI 编程最危险的时刻,往往不是它明显报错,而是代码看起来非常专业&#xff1a…

2026/7/31 19:23:06 阅读更多 →

日新闻

物理复制比逻辑复制好在哪?数据库复制原理详解

物理复制比逻辑复制好在哪?数据库复制原理详解

数据库复制是把主库数据同步到备库的机制,分为逻辑复制和物理复制两种。逻辑复制传输的是 SQL 语句或行变更事件,物理复制传输的是存储引擎底层的物理日志。阿里云 PolarDB(云原生数据库)采用物理复制,在同步延迟、数据…

2026/7/31 0:00:34 阅读更多 →
BilibiliDown:3分钟学会B站视频下载的终极指南

BilibiliDown:3分钟学会B站视频下载的终极指南

BilibiliDown:3分钟学会B站视频下载的终极指南 【免费下载链接】BilibiliDown (GUI-多平台支持) B站 哔哩哔哩 视频下载器。支持稍后再看、收藏夹、UP主视频批量下载|Bilibili Video Downloader 😳 项目地址: https://gitcode.com/gh_mirrors/bi/Bilib…

2026/7/31 0:00:34 阅读更多 →
有哪些游戏数据AI平台?游戏行业Data+AI融合方案盘点

有哪些游戏数据AI平台?游戏行业Data+AI融合方案盘点

当前,游戏行业的“DataAI融合”已从概念验证进入价值落地阶段。根据IDC 2025年数据,中国AI游戏云市场规模已达18.6亿元;同时,游戏研发环节AI渗透率高达86%,生成式AI内容普及率超过50%。面对庞大的市场,游戏…

2026/7/31 0:00:34 阅读更多 →

周新闻

深度学习道路桥梁裂缝检测系统 道路桥梁裂缝检测数据集 道路桥梁病害识别检测数据集

深度学习道路桥梁裂缝检测系统 道路桥梁裂缝检测数据集 道路桥梁病害识别检测数据集

深度学习道路桥梁裂缝检测系统 数据集6000张 完整源码已标注数据集训练好的模型环境配置教程程序运行说明文档,可以直接使用!系统支持图片、视频、摄像头等多种方式检测裂缝,功能强大实用。 1数据集6000张 8各类别

2026/7/31 1:03:03 阅读更多 →
深度学习YOLO模型如何训练 PUBG 绝地求生目标检测数据集

深度学习YOLO模型如何训练 PUBG 绝地求生目标检测数据集

pubg数据集 精选原图1.42万数据 1.49万标签 无任何重复、算法增强或冗余图像! pubg绝地求生目标检测数据集 1分类:e_body,14905个标签,txt格式 共计14244张图,99%为640*640尺寸图像 适合yolo目标检测、AI训练关键词&am…

2026/7/29 14:34:28 阅读更多 →
Apex英雄目标检测数据集 深度学习框架YOLO如何训练APEX数据集

Apex英雄目标检测数据集 深度学习框架YOLO如何训练APEX数据集

Apex检测数据集数据集详情检测类别: allies enemy tag图片总量:7247张训练集:5139张验证集:1425张测试集:683张标注状态:全部已标注,即拿即用数据格式:支持YOLO格式及其他格式&#…

2026/7/31 4:19:39 阅读更多 →

月新闻