1) 概念速览(最重要)
-
物体深度(object depth):指从摄像机到该物体表面某一点的距离(可以是像素级别的点或顶点)。例如
distance(CameraPosition, WorldPosition)
。这是“某个对象本身”的深度。 -
场景深度(scene depth):指深度缓冲(depth buffer)里记录的每个屏幕像素对应的最近几何体深度,也就是“在这个屏幕像素上场景中可见表面的深度”。用于判断像素间遮挡、合成、后处理等。
2) UE 中如何表示 / 哪里能拿到
-
在材质编辑器和后处理材质里,SceneDepth(或 SceneTexture: SceneDepth)节点可以读取“场景深度”——用来和当前像素/片元的深度比较。
-
Absolute World Position
、CameraPosition
、WorldPosition
等节点可以用来计算“物体深度”(直接用距离函数)。 -
在后处理(Post Process)或材质里可以同时使用两者做各种效果(深度测试、软粒子、深度雾、轮廓、SSAO、屏幕空间反射等)。
3) 在材质里实战:如何拿到两种深度(实用步骤)
A. 物体深度(像素级物体深度)
-
如果你在对象的材质内部,需要像素级距离:
-
ObjectDepth = distance(CameraPosition, AbsoluteWorldPosition)
-
在材质节点里用
CameraPosition
(节点)和Absolute World Position
(节点)算距离。适合非透明、要基于物体距离做过渡/渐隐的情况。
-
B. 场景深度(当前屏幕像素的场景深度)
-
在材质里用
SceneDepth
节点(传入UV或使用默认屏幕UV)得到场景深度值(常用于比较“场景表面深度 vs 当前片元深度”)。 -
在后处理材质可以直接使用 SceneTextureLookup(SceneDepth) 读取深度缓冲。
注意:不同情况下 SceneDepth 返回的深度表达可能需要“线性化”或转换到相同空间与单位(见下节)。
4) 深度的“线性化”与空间(非常重要)
-
深度缓冲通常以 非线性投影深度(clip/ndc depth) 存储(远处精度较低),所以直接比较有时会出错或不直观。
-
常见流程:
-
从
ScreenPosition
获得屏幕 UV / NDC。 -
用
SceneDepth(UV)
取出深度值(在一些 API/Pass 里是 NDC 深度,或是距离,需要根据上下文处理)。 -
若值是投影空间深度(非线性),需要用逆投影矩阵把它还原为 视空间 Z 或 世界空间位置,再做距离比较。
-
-
通用还原思路(HLSL 思路):
-
将屏幕 UV → NDC(
x,y
映射到 [-1,1])和深度扩展到 clip space Z。 -
用
InvProjectionMatrix
把 clip-space 点反投影回 view-space;再用InvViewMatrix
得 world-space(如果需要)。 -
也可以直接把 SceneDepth(如果引擎返回的是“世界单位距离”)与
distance(CameraPosition, AbsoluteWorldPosition)
直接比较 — 这最简单但要确认 SceneDepth 的单位。
-
(在材质编辑器中很多节点或 engine helper 已经把这些转换封装好;如果写 HLSL/Custom node,上面思路常用。)
5) 常见用例 + 快速实现技巧
1. 软粒子(Soft Particles)
-
问题:透明粒子在靠近几何体时出现硬切割。
-
做法:在粒子材质里读取
SceneDepth
,计算粒子片元到场景表面的距离(sceneDepth - objectDepth),根据距离做 alpha 混合或 fade(距离越小越透明)。UE 也有Depth Fade
节点可用于这种情况(Particle/Translucent 材质中常用)。
2. 深度雾 / 雾化(Depth-based Fog)
-
使用
distance(CameraPosition, WorldPosition)
或 SceneDepth 来控制颜色混合强度(根据与摄像机距离淡化颜色或混入雾色)。
3. 屏幕空间轮廓 / 边缘检测
-
在后处理里读取
SceneDepth
周围像素深度,做深度差阈值判定来检测边缘(SSAO、轮廓、法线边缘都可结合深度)。
4. 遮挡/合成(例如在后处理叠加 UI 到场景)
-
比较 SceneDepth 与 UI 元素计算出的深度来决定像素是否被遮挡,从而做正确的前后关系。
5. 从深度还原世界位置(用于光照、反射等)
-
在后处理材质里常用:从 screen UV 和 SceneDepth 反算出该像素的 world position,然后用于基于位置的效果(SSAO、屏幕空间光照修正、体积效果等)。
6) Material 编辑器中的常用节点(实践清单)
-
CameraPosition
:摄像机世界位置。 -
Absolute World Position
:当前片元的世界坐标。 -
SceneDepth
/SceneTexture: SceneDepth
:读取场景深度。 -
ScreenPosition
:屏幕坐标(UV / 粒子/后处理时用)。 -
DepthFade
:内置的深度渐隐(方便做软粒子边缘)。 -
Custom
node(HLSL):当需要逆投影或复杂处理时写小段 HLSL。 -
PixelDepth
(某些上下文)/SceneDepth
:确认你使用哪个深度源(在不同Pass结果不同)。
7) 常见坑与注意事项
-
单位/空间不一致:比较深度时务必确认两者在同一空间(世界距离 vs 非线性投影深度)。
-
透明/延迟渲染顺序:透明物体在渲染时可能看不到正确 SceneDepth(因为透明不写深度或在后序),导致深度比较异常。软粒子、透明材质要特别处理。
-
MSAA、多采样:深度读取在开启 MSAA 下可能需要额外考虑采样方式。
-
部分pass 不可用:在某些渲染通道(如阴影映射、某些 early passes)SceneDepth 不可用或不是你期望的值。
-
精度问题:深度缓冲精度在远处会下降,可通过线性化和/或使用双精度技巧(或调整 near/far)缓解。
-
性能:频繁做屏幕空间深度采样、反投影、多个邻域采样(edge detection / blur)会比较贵,注意性能剖析(Profile)。
8) 简短代码片段(思路级 HLSL,用于 Custom 节点或后处理)
(说明:这是通用反投影思路,具体 API 名称依你写的位置而异)
// 输入:float2 uv (0..1), float sceneDepth = SceneDepth(uv); // 把 depth 从 [0,1](clip depth)转换回 NDC space [-1,1] 的 z: float z_ndc = sceneDepth * 2.0 - 1.0; // 构造 clip space 点(x,y从uv转换),w=1 float4 clip = float4(uv * 2.0 - 1.0, z_ndc, 1.0); // 反投影到 view space float4 viewPos = mul(InvProjectionMatrix, clip); viewPos /= viewPos.w; // 如果需要 world pos: worldPos = mul(InvViewMatrix, viewPos);
(在 UE 的材质 Custom 节点里你可能需要把 InvProjectionMatrix
与 InvViewMatrix
作为全局参数传入,或者使用引擎提供的 helper。)
9) 快速示例:实现软粒子(Material 节点步骤)
-
在粒子材质里:拿到
ScreenPosition
→ 得到UV
。 -
用
SceneDepth(UV)
读取场景深度。 -
计算
objectDepth = distance(CameraPosition, AbsoluteWorldPosition)
。 -
depthDiff = sceneDepth - objectDepth
。 -
用
saturate(depthDiff / fadeRange)
作为 alpha 混合权重(fadeRange 控制软化距离)。 -
将 alpha 乘以该权重,输出到 opacity(或用在透明度测试里)。
10) 总结(记住这两句)
-
物体深度 = “摄像机 ↔ 该点(物体)的真实距离”。
-
场景深度 = “深度缓冲里记录的:当前屏幕像素上最前面的表面深度”。
两者搭配可以做很多屏幕空间特效(软粒子、深度雾、遮挡合成、边缘检测、SSAO 等),但务必关心空间一致性(world/view/clip)和深度线性化问题。