
1. 深度图与Mask图到底在UE5里扮演什么角色1.1 从一个实际需求说起很多人在UE5里做后期效果时第一个想到的是拿SceneTexture节点去采样但一旦涉及“只对特定物体生效”“根据距离做渐变”“把场景深度转成可视化灰度图”这类需求就会发现默认的后处理材质根本不够用。我最初接触深度图和Mask图是因为一个工业可视化项目需要在场景中做“透视墙体”效果——当摄像机靠近某个设备时该设备外壳自动半透明露出内部结构。这个需求听起来简单但真正落地时涉及三个核心问题怎么拿到每个像素的深度值、怎么区分哪些像素属于目标物体、怎么把这两个信息传给后处理材质做混合。深度图和Mask图就是解决这类问题的两把钥匙。深度图记录的是场景中每个像素到摄像机的距离信息Mask图则是一张“选择性标记”的图告诉渲染管线哪些区域需要特殊处理。两者结合可以实现景深、雾效、轮廓描边、物体高亮、透视切换、距离淡入淡出等大量高级效果。1.2 深度图的本质不是一张“图片”很多人第一次听到“深度图”会以为它是一张存在硬盘上的灰度图实际上在UE5的实时渲染管线里深度信息是以深度缓冲区Depth Buffer的形式存在于显存中的。每个像素对应一个浮点值表示该像素对应的场景表面到摄像机近裁剪面的距离。这个值经过投影矩阵变换后是非线性的靠近摄像机的精度高远处的精度低——这是透视投影的固有特性。在材质编辑器里你可以通过SceneTexture:SceneDepth节点拿到当前像素的深度值通过SceneTexture:CustomDepth拿到自定义深度。两者的区别在于SceneDepth是场景所有不透明物体自动写入的CustomDepth则需要你在物体上手动开启Render CustomDepth Pass选项。这个区别非常关键后面会反复用到。1.3 Mask图的本质选择性渲染的开关Mask图在UE5里没有单一的官方节点叫这个名字它更多是一种技术思路通过某种方式生成一张“哪些像素需要处理”的标记信息然后在后处理材质里用这个标记做分支。常见的Mask来源有三种CustomDepth CustomStencil在物体上开启CustomDepth并设置Stencil Value后处理材质里通过SceneTexture:CustomStencil读取判断值是否等于目标值来生成Mask。材质ID通道通过SceneTexture:MaterialID或者自定义的GBuffer通道来区分不同材质类型的区域。屏幕空间计算直接用像素坐标、深度值、法线等信息在材质里实时计算出一个Mask比如根据深度差做边缘检测。三种方式各有适用场景。Stencil方式最精确、性能最好但需要提前在物体上配置屏幕空间计算最灵活但容易受分辨率影响产生锯齿。1.4 谁需要掌握这些内容如果你正在做以下几类工作深度图和Mask图几乎是绕不开的建筑可视化中的剖切、透视、距离标注游戏中的描边、交互高亮、瞄准镜效果工业仿真中的设备拆解、内部结构展示影视预演中的景深控制、雾效分层不需要你有多深的图形学背景但至少要能看懂材质编辑器里的节点连线知道向量、点积、插值这些基础概念。下面我会从项目配置开始一步步把完整流程拆开讲。2. 项目配置与前置准备别急着连节点2.1 项目设置里必须打开的几个开关在UE5里做深度相关的后处理第一步不是打开材质编辑器而是检查项目设置。我见过太多人卡在“为什么CustomDepth永远是0”这种问题上最后发现是项目配置没开。打开Edit Project Settings找到Engine Rendering确认以下几项设置项推荐值作用Custom Depth-Stencil PassEnabled with Stencil开启CustomDepth和Stencil写入Support Compute Skin Cache按需骨骼网格体需要CustomDepth时开启Post Processing保持默认后处理材质的基础支持Custom Depth-Stencil Pass这个选项有三个值Disabled、Enabled、Enabled with Stencil。如果你只需要CustomDepth不需要Stencil选Enabled就行但如果要做Mask必须选Enabled with Stencil否则Stencil Value永远是0。注意这个设置修改后需要重启编辑器才完全生效别改完就直接测试然后怀疑人生。2.2 后处理材质的基础创建流程在Content Browser里右键选择Material创建后打开。在材质细节面板里把Material Domain改为Post Process。这一步做完之后材质节点里的Emissive Color就变成了最终输出到屏幕的颜色。接着把Blendable Location设置好。这个选项决定了后处理材质在渲染管线的哪个阶段执行Before Tonemapping在色调映射之前适合做深度、雾效这类需要线性空间计算的效果After Tonemapping在色调映射之后适合做颜色校正、UI叠加Before Translucency在透明物体渲染之前做深度和Mask相关效果绝大多数情况选Before Tonemapping。原因是深度值是线性的色调映射会改变数值关系放在之后算容易出问题。2.3 把后处理材质挂到场景里材质做好之后需要在场景里放一个PostProcessVolume。在Outliner里右键选择Volumes Post Process Volume然后在细节面板里找到Rendering Features Post Process Materials把刚才创建的材质加进去。这里有个容易忽略的点PostProcessVolume有个Infinite Extent (Unbound)选项。勾上之后这个体积会影响整个场景不勾的话只有摄像机进入体积范围内才生效。做全局效果就勾上做局部效果就不勾然后调整体积的Brush形状。2.4 验证深度信息是否正常在正式做效果之前先做一个最简单的验证在后处理材质里连一个SceneTexture:SceneDepth节点输出到Emissive Color然后除以一个较大的数比如10000来归一化。如果场景显示出一张近处亮、远处暗的灰度图说明深度信息正常。如果全黑或者全白检查以下几点后处理材质是否真的挂到了PostProcessVolume上Blendable Location是否设置正确场景里是否有不透明物体透明物体默认不写入SceneDepth这个验证步骤看起来简单但能帮你排除掉80%的基础配置问题。3. 深度图的核心操作与参数计算3.1 SceneDepth节点的输出到底是什么SceneTexture:SceneDepth输出的是一个浮点值单位是厘米UE默认单位。但这个值不是线性的它经过了投影变换。具体来说深度缓冲区里存的值和实际距离的关系是depthBufferValue (far * near) / (far - distance * (far - near))其中near和far是摄像机的近远裁剪面。这个公式意味着靠近摄像机的像素深度值变化剧烈远处的像素深度值变化平缓。如果你直接拿这个值做线性插值会发现近处效果很敏感远处几乎没变化。3.2 把非线性深度转成线性距离要得到真实的线性距离需要用SceneTexture:SceneDepth配合摄像机参数做逆变换。UE5提供了一个更简单的方式使用SceneTexture:SceneDepth节点然后在材质里用以下公式转换linearDepth (near * far) / (far - sceneDepth * (far - near))但实际在材质编辑器里手连这个公式比较麻烦。更实用的做法是直接用SceneTexture:SceneDepth减去一个基准值然后除以一个范围值来做归一化。比如你想让10米以内的物体有渐变效果normalizedDepth saturate((sceneDepth - 1000) / 2000)这里1000是10米UE单位是厘米2000是20米的过渡范围。这样算出来的值在0到1之间0表示10米以内1表示30米以外。3.3 深度边缘检测做描边和轮廓的基础深度图最有价值的应用之一是边缘检测。原理很简单当前像素的深度和周围像素的深度做差差值超过阈值就认为是边缘。在材质里实现时用SceneTexture:SceneDepth节点把UV偏移几个像素分别采样然后做差。UE5的材质编辑器里可以用TextureCoordinate节点加上一个偏移量来实现。具体参数计算假设屏幕分辨率是1920x1080你想检测1像素宽度的边缘UV偏移量就是1/1920和1/1080。但实际做描边时通常需要2到3像素的宽度所以偏移量要乘以2或3。// 伪代码示意 float2 uv GetScreenUV(); float2 offset float2(1.0/1920, 1.0/1080) * edgeWidth; float depthCenter SceneDepth(uv); float depthLeft SceneDepth(uv - float2(offset.x, 0)); float depthRight SceneDepth(uv float2(offset.x, 0)); float depthUp SceneDepth(uv - float2(0, offset.y)); float depthDown SceneDepth(uv float2(0, offset.y)); float edge abs(depthCenter - depthLeft) abs(depthCenter - depthRight) abs(depthCenter - depthUp) abs(depthCenter - depthDown); edge edge threshold ? 1 : 0;这个逻辑在材质编辑器里用节点连出来大概需要十几个节点但理解了原理之后并不复杂。3.4 深度雾效比指数雾更可控的方案UE5自带的指数高度雾很好用但有时候你需要更精细的控制比如“只对某个深度范围加雾”“雾的颜色随距离变化”。这时候用深度图做自定义雾效就更灵活。核心思路用线性深度值作为插值系数在原始场景颜色和雾颜色之间做Lerp。材质里先采样SceneTexture:PostProcessInput0拿到场景颜色然后fogFactor saturate((linearDepth - fogStart) / (fogEnd - fogStart)); finalColor lerp(sceneColor, fogColor, fogFactor);fogStart和fogEnd根据场景尺度调整。做建筑可视化时fogStart可以设成500050米fogEnd设成20000200米这样远处建筑会逐渐融入雾色近处保持清晰。实操心得雾颜色不要用纯白或纯黑用场景环境光的平均色稍微偏一点效果更自然。我通常会用天空光的颜色乘以0.8作为雾色。4. Mask图的生成与高级应用4.1 CustomStencil的完整配置流程Stencil是做Mask最精确的方式。完整流程如下第一步在场景里选中需要标记的物体在细节面板找到Rendering Render CustomDepth Pass勾上。然后在下面找到CustomDepth Stencil Value设置一个1到255之间的整数。比如所有需要高亮的设备设为1需要透视的墙体设为2。第二步在后处理材质里添加SceneTexture:CustomStencil节点。这个节点输出的是当前像素的Stencil值是一个整数。第三步用If节点或者减法加Abs的方式判断Stencil值是否等于目标值。比如判断是否等于1mask 1 - saturate(abs(stencilValue - 1));这样当stencilValue等于1时mask为1不等于1时mask为0。第四步用这个mask去控制颜色混合、透明度或者任何你想要的效果。4.2 Stencil值与物体分组的策略实际项目里往往有多个物体需要不同的处理。比如一个工厂场景里管道需要高亮、阀门需要闪烁、墙体需要透视。这时候Stencil值的分配就很重要。我的习惯是做一个表格来管理Stencil值用途对应物体1高亮描边所有交互设备2透视墙体建筑外墙3距离标注需要显示距离的物体4特殊效果预留这个表格要同步给所有参与项目的人否则后期维护会乱。UE5里没有集中的Stencil管理面板全靠手动设置所以文档很重要。4.3 用深度差生成动态MaskStencil需要提前配置但有些场景下你需要动态生成Mask。比如“所有距离摄像机5米以内的物体自动高亮”这时候就用深度差来做。思路是先算出线性深度然后用smoothstep或者saturate生成一个基于距离的MaskdistanceMask 1 - saturate((linearDepth - 500) / 200);这样5米以内的物体distanceMask接近17米以外接近0中间平滑过渡。这个Mask可以直接用来做高亮或者颜色叠加。更高级的用法是结合法线信息。比如“只高亮朝向摄像机的表面”可以用SceneTexture:WorldNormal和摄像机方向做点积点积结果大于0.7的像素才纳入Mask。这样背面的像素不会被高亮效果更干净。4.4 Mask图的抗锯齿处理直接用Stencil或者深度差生成的Mask边缘会有锯齿因为它是基于像素的硬判断。解决方法有两种第一种是软化边缘。不用If做二值判断而是用smoothstep做平滑过渡。比如mask smoothstep(0.4, 0.6, stencilValue);这样Stencil值在0.4到0.6之间时Mask会渐变边缘就柔和了。第二种是配合SceneTexture:PostProcessInput0做屏幕空间的抗锯齿。UE5的TAA时间抗锯齿会自动处理大部分边缘但后处理材质里的硬边缘TAA处理不了。所以还是推荐第一种方式在材质层面就把边缘做软。注意smoothstep的范围不要设太大否则Mask会糊成一片。一般0.1到0.2的过渡范围就够了。5. 完整实战案例设备透视与距离标注系统5.1 案例需求拆解假设有一个工业设备展示场景需求如下摄像机靠近设备时设备外壳自动半透明露出内部结构设备上方显示到摄像机的实时距离距离小于3米时设备轮廓高亮距离大于10米时设备淡出这个案例涵盖了深度图、Mask图、距离计算、条件渲染等多个核心知识点。5.2 场景准备与物体配置首先在场景里放置设备模型每个设备是一个独立的Static Mesh Actor。选中所有需要透视的设备外壳开启Render CustomDepth PassStencil Value设为1。内部结构不需要特殊处理保持默认。然后在场景里放一个PostProcessVolume勾选Infinite Extent (Unbound)把后面做的后处理材质加进去。5.3 后处理材质节点连线详解材质里需要计算以下几个值线性深度计算用SceneTexture:SceneDepth节点输出连到一个Divide节点除以10000做归一化。这个值用来做距离判断。Stencil Mask用SceneTexture:CustomStencil节点减去1取绝对值再用1 - saturate得到Mask。这个Mask标记了所有外壳像素。距离渐变用线性深度值做smoothstep范围从300到5003米到5米得到一个0到1的渐变值。这个值用来控制外壳的透明度。高亮Mask线性深度小于300时结合Stencil Mask生成高亮区域。最终混合用Lerp节点以距离渐变为系数在原始场景颜色和高亮颜色之间插值。再用Stencil Mask控制只有外壳像素参与混合。节点连线大概需要30到40个节点但逻辑是清晰的先算深度再算Mask最后混合。5.4 距离标注的UI实现距离标注用后处理材质做不太合适因为文字渲染需要UMG。更实用的方案是用Widget Component挂在设备Actor上然后在Tick里计算摄像机到Actor的距离更新Widget里的Text。// C伪代码 void AEquipmentActor::Tick(float DeltaTime) { Super::Tick(DeltaTime); if (DistanceWidget CameraActor) { float Distance FVector::Dist(GetActorLocation(), CameraActor-GetActorLocation()); DistanceWidget-SetDistance(Distance / 100.0f); // 转成米 } }Widget的朝向用FaceCamera模式这样无论摄像机怎么转文字始终面向屏幕。5.5 性能优化与参数调优后处理材质的性能开销主要来自SceneTexture采样。每采样一次SceneDepth或CustomStencil就是一次全屏纹理读取。节点越多开销越大。优化策略合并采样能用一次采样算出来的值不要采样两次降低精度SceneDepth用Point采样而不是Bilinear减少带宽限制范围在材质里加if分支距离太远的像素直接跳过计算实测下来一个包含5次SceneTexture采样的后处理材质在1080p下大概增加0.5到1毫秒的GPU时间。如果场景里还有其他后处理效果需要留出余量。优化项优化前优化后收益SceneDepth采样次数5次3次减少40%带宽采样过滤模式BilinearPoint减少50%纹理读取分支裁剪无距离10000跳过远处像素零开销6. 常见问题与排查技巧实录6.1 CustomDepth全黑或全白这是最常见的问题。排查顺序项目设置里Custom Depth-Stencil Pass是否开启物体是否勾选Render CustomDepth Pass后处理材质的Blendable Location是否设置正确PostProcessVolume是否勾选Infinite Extent如果以上都正常检查物体是不是透明材质。透明物体默认不写入CustomDepth需要在材质里把Allow Custom Depth Writes打开。6.2 Stencil值读取始终为0Stencil值读取为0通常是因为项目设置里选的是Enabled而不是Enabled with Stencil。这两个选项差一个单词但结果完全不同。改完之后记得重启编辑器。另一个可能的原因是物体用了Instanced Static MeshStencil值需要在Instanced Static Mesh Component上设置而不是在单个Instance上。6.3 后处理材质不生效后处理材质不生效的原因比较多按以下顺序排查材质Domain是否为Post Process材质是否编译成功看材质编辑器右下角有没有错误PostProcessVolume的Post Process Materials数组里是否添加了材质Volume的Priority是否被其他Volume覆盖摄像机是否在Volume范围内如果没勾Unbound6.4 深度边缘检测出现噪点深度边缘检测对噪声很敏感尤其是远处物体。解决方法增大边缘检测的阈值过滤掉微小深度差对深度值先做一次模糊再检测限制检测范围只对一定距离内的像素做边缘检测我通常会把阈值设在50到100之间UE单位低于这个值的深度差认为是噪声。6.5 性能突然下降后处理材质性能下降通常是因为采样次数过多用了SceneTexture:SceneColor这种高开销节点材质里用了循环或复杂数学运算用ProfileGPU命令可以查看具体是哪个Pass耗时。如果确认是后处理材质的问题逐步注释节点定位。6.6 常见问题速查表现象可能原因解决方法CustomDepth全黑项目设置未开启开启Custom Depth-Stencil PassStencil值为0选错Pass模式改为Enabled with Stencil后处理不生效Volume未配置检查PostProcessMaterials数组边缘有锯齿Mask未软化用smoothstep替代If远处效果异常深度精度不足调整近远裁剪面性能下降采样过多合并采样、降低精度6.7 独家避坑经验第一个坑不要在后处理材质里做太多逻辑判断。材质编辑器里的If节点在GPU上并不是真正的分支两个分支都会执行只是最后选一个结果。所以用If省性能是错觉真正省性能的是减少采样和数学运算。第二个坑深度值的单位是厘米不是米。UE默认单位是厘米所以1米等于100。做距离计算时如果忘了换算效果会差100倍。第三个坑PostProcessVolume的Priority。场景里如果有多个VolumePriority高的会覆盖低的。调试时如果发现效果时有时无检查一下是不是被其他Volume覆盖了。第四个坑移动端不支持CustomStencil。如果项目要发布到移动平台Stencil方案直接用不了只能用深度差或者MaterialID来做Mask。这个限制在项目初期就要考虑清楚。7. 进阶方向从深度图到体积渲染7.1 深度图与体积雾的结合UE5的体积雾系统本身就依赖深度信息。如果你想做自定义的体积效果比如“只在特定深度范围内显示体积光”可以用深度图生成一个Mask然后在体积雾材质里用这个Mask控制密度。具体做法是在体积雾材质里采样SceneTexture:SceneDepth算出线性深度然后用smoothstep生成一个深度范围的Mask乘以雾的密度。这样就能实现“雾只出现在10米到20米之间”这种效果。7.2 深度图做屏幕空间反射的辅助屏幕空间反射SSR在UE5里是内置的但有时候反射效果不理想。用深度图可以做一层辅助在反射强烈的区域用深度差检测出反射边缘然后叠加一层高光。这个技巧在建筑可视化里很常用能让玻璃幕墙的反射更锐利。7.3 Mask图与材质分层渲染Mask图不仅可以做后处理还可以在材质层面做分层。比如一个物体表面需要根据距离显示不同的纹理可以用深度Mask在材质里做Lerp近处显示细节纹理远处显示简化纹理。这种方式比传统的Mipmap更灵活因为你可以自定义每一层的过渡范围。7.4 性能与效果的平衡点深度图和Mask图的效果很强大但性能开销是实打实的。我的经验是在1080p分辨率下一个包含3到5次SceneTexture采样的后处理材质GPU开销在0.5到1.5毫秒之间。如果目标帧率是60帧16.6毫秒预算这个开销占比大概3%到9%是可以接受的。但如果项目要上4K采样开销会翻4倍这时候就需要做分辨率缩放或者只在局部区域做后处理。UE5的Screen Percentage设置可以降低后处理的分辨率效果上会有轻微模糊但性能提升明显。实操建议在项目初期就把后处理材质的性能预算定下来比如“所有后处理效果加起来不超过2毫秒”。这样在加效果时就有取舍依据不会到最后才发现性能扛不住。深度图和Mask图这套东西入门不难但要做到性能好、效果稳、可维护需要在实际项目里反复调。我自己的习惯是每做一个新效果先在一个空场景里验证核心逻辑确认没问题再往正式场景里搬。这样排查问题时变量少效率高很多。