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/10/17 5:25:57 阅读更多 →
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/10/16 13:37:53 阅读更多 →
大疆 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/10/17 12:52:03 阅读更多 →

最新新闻

SPC开发包源代码实战:控制图与过程能力计算落地指南

SPC开发包源代码实战:控制图与过程能力计算落地指南

简介:这份SPC开发包源代码面向工业自动化与现场总线方向的嵌入式开发者,聚焦Profibus-DP从站通信的底层实现,适合已具备C语言基础、希望深入理解协议栈细节的中高级工程师。压缩包共4个文件,包含3个c源文件与1个h头文件&#xff0…

2026/10/17 17:12:18 阅读更多 →
人工智能导论期末冲刺:知识表示、归结原理与可信度计算全攻略

人工智能导论期末冲刺:知识表示、归结原理与可信度计算全攻略

简介:这是一份山东大学《人工智能导论》课程概念汇总PDF,适合正在备考期末、考研复试或系统梳理AI基础概念的本科生与自学读者。内容以章节为主线,从智能、知识、智能特征出发,梳理人工智能定义与发展阶段;随后系统整理…

2026/10/17 17:12:18 阅读更多 →
Serv-U 14.0白金版实战:老文件服务器安全升级与多协议配置指南

Serv-U 14.0白金版实战:老文件服务器安全升级与多协议配置指南

简介:Serv-U 14.0 白金版是一款面向专业用户与企业级场景的 FTP 服务器软件,主打高效、安全的文件传输与稳定的服务运营。该资源包聚焦其界面与传输历史记录相关的前端素材,适合正在部署或维护 FTP 服务、需要了解历史记录模块实现方式的运维…

2026/10/17 17:12:17 阅读更多 →
claude-code-templates 不是 CLI:本地代码知识库的正确打开方式

claude-code-templates 不是 CLI:本地代码知识库的正确打开方式

1. 这不是又一个 CLI 工具:Claude-Code-Templates 的真实定位与误用陷阱“claude-code-templates”——光看这个名字,绝大多数人第一反应是:“哦,Anthropic 官方出的 Claude 代码生成 CLI?”或者“是不是类似 Copilot …

2026/10/17 17:12:17 阅读更多 →
光伏功率预测新框架:VMD-RIME-LSTM原理与Matlab实战

光伏功率预测新框架:VMD-RIME-LSTM原理与Matlab实战

现在到处都能看到光伏功率预测的论文,组合模型一个比一个花哨,但很多同学卡在同一个地方:方法选了一堆,真正能跑出干净结果、还能把论文故事讲完整的没几个。最近被问得最多的就是"变分模态分解霜冰算法优化LSTM"这条路…

2026/10/17 17:12:17 阅读更多 →
Unity虚拟装配中的机械运动仿真:从可视化到工程验证

Unity虚拟装配中的机械运动仿真:从可视化到工程验证

1. 这不是“转个齿轮”那么简单:虚拟装配仿真里藏着的机械逻辑陷阱很多人第一次在Unity里拖进一个齿轮模型,加个Rigidbody和HingeJoint,再写几行代码让电机轴转起来,看到齿轮咬合转动,就以为“机械运动仿真”这事搞定了…

2026/10/17 17:11:17 阅读更多 →

日新闻

DeepSeek选型与部署:V3/R1、Ollama、API全攻略

DeepSeek选型与部署:V3/R1、Ollama、API全攻略

简介:《DeepSeek使用方法.pdf》是一份面向DeepSeek入门及进阶用户的完整使用指南,重点围绕V3与R1两款模型的能力差异与适用场景展开,帮助读者根据任务类型与成本选择合适的模型。资源共1个PDF文件,压缩包大小约311KB,篇…

2026/10/17 0:00:25 阅读更多 →
200+Deepseek润色指令拆解:从底层原理到场景落地的完整指南

200+Deepseek润色指令拆解:从底层原理到场景落地的完整指南

简介:这是一份聚焦DeepSeek提示词优化的《200Deepseek润色指令》PDF文档,共收录两百余条可直接套用的润色指令,涵盖语法修正、风格统一、逻辑梳理、表达精炼等维度,并针对商务写作、学术论文、广告文案等场景给出差异化建议。文档…

2026/10/17 0:00:25 阅读更多 →
OpenCV轮廓法物体计数系统:轻量、可控、可落地的工业级实现

OpenCV轮廓法物体计数系统:轻量、可控、可落地的工业级实现

简介:本资源是一份基于OpenCV的本科课程设计完整报告,面向计算机视觉初学者、图像处理学习者及高校通信/信息类专业学生,解决物体智能计数与检测结果结构化记录的实际问题。项目覆盖图像预处理、轮廓检测、边界框绘制、轨迹栏交互式参数调节、…

2026/10/17 0:00:25 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/16 21:35:51 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/17 1:44:59 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/16 15:04:24 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/16 21:54:38 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/16 3:49:43 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/16 14:23:36 阅读更多 →