新闻详情

新闻详情

首页 / 资讯中心 / 详情

Unity屏幕空间后处理:深度与法线实现轮廓描边与幽灵遮挡

发布时间:2026/9/4 20:04:20来源:尧图网络
Unity屏幕空间后处理:深度与法线实现轮廓描边与幽灵遮挡
Unity 的屏幕空间后处理最直接的价值不是炫技而是把“物体轮廓”和“幽灵遮挡提示”这类交互反馈放进同一套深度信息里统一处理。选中敌人时描边、被墙挡住的队友显示半透明幽灵轮廓、场景里机关和关键道具被遮挡时还能看到大致位置这些需求在游戏项目里出现频率非常高。这篇内容就按我自己的调试顺序把轮廓和幽灵遮挡效果的实现思路、环境条件、核心代码、参数调整和踩坑点完整拆一遍。先说结论这类效果看着像是两种技术底层其实都依赖深度缓冲。轮廓效果靠的是深度和法线的突变判断边界幽灵遮挡效果靠的是物体与场景深度之间的前后关系。理解了这两条主线后面不管是做敌人高亮、队友提示、还是物品可见性思路都是通的。1. 效果拆分清楚再决定用哪一套渲染方案1.1 轮廓效果解决的是“边界的可见性”轮廓描边不是简单把物体画一个粗线框。在屏幕上看到的“轮廓”本质是物体与背景之间出现了可以被识别的深度差或法线差。后处理的做法是先拿到整张画面的深度纹理解析梯度再把梯度明显的像素替换成高亮颜色。由于这个判断发生在屏幕空间不依赖物体网格拓扑所以不管是角色、载具、机械还是飘动的粒子只要它和周围环境在深度或法线上有明显差异就会被勾出来。这也意味着轮廓效果天然有一个限制如果两个物体深度非常接近、法线方向也一致例如同一个平面上贴了一张颜色完全不同的贴图轮廓后处理无法单独靠深度和法线识别出颜色边界。想要那种“按贴图内容描边”的效果得改用 Image Effect 先做颜色边缘检测或者引入额外的物体 ID 缓冲。实际做游戏交互提示时我还是优先用深度和法线因为它稳定不受纹理和灯光影响而且性能开销可控。1.2 幽灵遮挡效果解决的是“被挡住后的可见性”当我们说“幽灵遮挡”时通常指的是目标物体在视觉上被墙、地形、门或其他物体遮挡系统仍然用半透明或发光轮廓提示它的大致位置。玩家希望知道“墙后面有个敌人虽然我看不见但我知道他在哪里”。这个时候不能直接把目标物改成全透明否则玩家完全失去信息也不能直接把遮挡物去掉否则玩家会发现穿墙视角甚至破坏关卡设计。比较常见的技术路线是把目标物用单独的通道再渲染一次通过深度比较让被墙遮挡的部分以特殊材质穿透显示或者在后处理阶段用目标物深度和场景遮挡深度做区域判断再给该区域叠加一层光晕和半透明轮廓。1.3 看起来不同的需求最终都要落到深度比较上无论是深度突变做描边还是遮挡关系判断做穿透提示关键其实就几个问题被判断物体的深度存在哪里场景不透明物体的深度存在哪里法线是否参与边界判断需要的是描边颜色还是整体半透明幽灵体还是两者叠加。我建议在动手写 Shader 前先定义一个最简单的测试场景一面墙、一个目标物体、一台主相机、一盏平行光。先让效果在这个场景里跑通再放进正式关卡。直接拿美术资源调试会浪费时间你很难判断是效果参数问题还是模型、材质、LOD 或遮挡剔除造成的问题。2. 前置条件管线、纹理开关和后处理挂载方式2.1 先确认项目用的是 Built-in 还是 URPUnity 后处理在不同渲染管线里挂载方式差别很大。老项目大多还在 Built-in 管线配合 Post Processing Stack v2新项目则更常用 URP。URP 里已经没有传统意义的OnRenderImage你需要通过ScriptableRendererFeature创建自定义 Render Pass再在 Renderer 资产里挂载。不同 URP 版本的 API 也在变化。例如很早的版本使用cmd.Blit后来的版本倾向用Blitter或RTHandle管理临时纹理。我的建议是如果只是学习优先用项目本身接近的 URP 版本跑通如果要参考网上的代码第一件事是看它对应的 URP 版本不要只看思路。2.2 必须开启的深度与法线选项要让屏幕空间 Shader 拿到正确的深度信息先确认以下配置配置项Built-in 管线URP深度纹理Camera 组件勾选 Depth Texture或通过 DepthTextureMode 开启URP Asset 中 Enable Depth Texture法线纹理自定义DepthNormals相机模式URP 的 DepthNormals Pass 或者自己用深度重建法线后处理挂载Post-processing Volume Custom EffectRenderer Feature Volume Override临时 RT 管理RenderTexture 手动释放RTHandle 或临时纹理资源如果项目已经有普通的后处理流程比如泛光、色调映射、景深建议把屏幕空间轮廓的 Pass 放在这些效果之后还是之前需要提前想清楚。通常描边颜色希望不被后续泛光吃掉太厉害描边 Pass 可以放在后处理栈中偏后的位置如果希望描边参加场景泛光那就放到半透明渲染之后、后处理栈进入前。没有绝对答案取决于美术想要的最终观感。2.3 相机剔除和层设置是隐形条件轮廓后处理通常作用在整张画面不会区分层。这意味着如果你只想给特定物体描边不能只靠后处理还需要配合目标 mask、深度标记或专用相机。幽灵遮挡效果更是这样通常只允许特定层或者特定 Tag 的物体参与穿透渲染否则整关的墙面和角色都会变成幽灵画面会非常混乱。我一般会把目标物的层单独列出来例如OutlineTarget或者GhostTarget。渲染主画面时正常渲染这些层然后在后处理或额外 Pass 中根据层和深度数据做合成。这样场景里的普通 NPC、背景物件都不受影响只对需要的目标生效。3. 先跑通“基础屏幕空间轮廓”用 Depth Normal 做边缘检测3.1 最小的 URP Renderer Feature 结构下面这段是示意结构重点不是让你直接复制到项目就编译通过而是理解一个自定义后处理 Pass 需要哪些部分。using UnityEngine; using UnityEngine.Rendering; using UnityEngine.Rendering.Universal; public class ScreenSpaceOutlineFeature : ScriptableRendererFeature { public Material outlineMaterial; public RenderPassEvent passEvent RenderPassEvent.AfterRenderingTransparents; private OutlineRenderPass outlinePass; public override void Create() { outlinePass new OutlineRenderPass(outlineMaterial, passEvent); } public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData) { if (outlineMaterial ! null) { renderer.EnqueuePass(outlinePass); } } } public class OutlineRenderPass : ScriptableRenderPass { private Material material; public OutlineRenderPass(Material mat, RenderPassEvent evt) { material mat; renderPassEvent evt; } public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData) { // 这里执行 Blit // 1. 从当前相机目标颜色缓冲取到源纹理 // 2. 用 outlineMaterial 做全屏绘制 // 3. 把结果写回相机目标 } }这个结构里最关键的是Execute方法。因为不同 URP 版本对源、目标、临时纹理的写法可能不同我建议在新建 Feature 时先打开 Unity 官方 URP 示例包里的 FullScreen Pass 或 Blit 示例把那段代码作为模板替换而不是从旧博客里抄 API。3.2 边缘检测 Shader 的核心思路屏幕空间轮廓的经典做法就是四邻域采样。每个像素在自己的 UV 位置采样一次深度和法线然后向左、右、上、下各偏移一个纹素采样。偏移量通常和当前屏幕分辨率有关_MainTex_TexelSize.xy就是单个纹素大小。伪代码如下float depthCenter SampleSceneDepth(uv); float3 normalCenter SampleSceneNormals(uv); float depthLeft SampleSceneDepth(uv - texelOffset); float depthRight SampleSceneDepth(uv texelOffset); float depthUp SampleSceneDepth(uv float2(0, texelOffset.y)); float depthDown SampleSceneDepth(uv - float2(0, texelOffset.y)); float depthDiffX abs(depthLeft - depthRight); float depthDiffY abs(depthUp - depthDown); float normalDiffX 1.0 - dot(normalLeft, normalRight); float normalDiffY 1.0 - dot(normalUp, normalDown);判断边缘时深度差不是简单使用 raw depth因为深度值是非线性的距离相机越远同样物理距离对应的深度差越小。实际使用时要先通过相机参数还原成线性深度或对深度差乘以距离相关的系数。很多教程里看到的_DepthThreshold参数本质上就是线性深度差阈值。法线参与判断的作用是解决“深度完全不变但是朝向不同”的情况。例如墙角处左右两边的墙面和地板的深度在同一屏幕点没明显突变但法线方向差异很大。加入法线参与就能把这类几何棱角也描出来。代价是需要相机能够提供法线数据。如果 URP 没有自动生成法线纹理可以考虑在相机上设置DepthNormals或者用深度重建世界坐标再算屏幕空间法线。3.3 可调参数和实际效果之间的对应关系参数作用调小的现象调大的现象Depth Threshold深度突变判定阈值边缘更容易出现噪声多只有深度变化很大的边界才描边比如人物和墙壁Normal Scale法线差异参与程度几乎忽略转角墙角、圆柱侧面都会有边缘Line Width采样偏移距离线条细线条粗但可能断Outline Color最终描边颜色弱覆盖感更强这里有个容易踩的问题Line Width 不是直接控制像素宽度而是控制采样 UV 的距离。一般不要超过 4因为采样距离拉开之后边缘会不稳定而且相邻物体的边缘容易连成一片变成一团光晕。3.4 验证“跑通”的标准第一次验证不要看美术效果先检查以下三点屏幕里有没有明显检测到目标物体和墙壁之间的交界线目标物体自身的正反面交界处是否出现预期描边摄像机旋转时描边是否出现大幅度闪烁或断裂。如果只是旋转视角就闪得厉害通常是因为轮廓检测只采样了中心像素周围极小的邻域目标边缘在屏幕上的梯度不够稳定。可以适当把 Line Width 调到 1 或 2同时将深度阈值调低一点看看。如果描边整体偏移或者出现局部空白先怀疑后处理 Blit 的 UV 方向和纹理坐标翻转不要急着调算法。屏幕坐标和 UV 的翻转在不同平台上表现不一致尤其是 OpenGL 和 DirectX 之间最终目标纹理坐标可能差一个 Y 翻转。遇到这种问题可以在 Shader 里输出 UV 本身作为颜色观察上下左右是否和屏幕一致。4. 把“幽灵遮挡”拆成两层正常可见 被遮挡后显示4.1 不能用同一个透明材质直接解决所有情况幽灵遮挡效果如果只用一个 shader 解决很容易出现两种错误目标物完全可见时也带透明发光看起来像贴图错误目标物被墙挡住时完全没有提示因为透明渲染乱序目标物根本没进入屏幕。真正稳妥的思路是拆成两层渲染正常渲染层目标物没被遮挡时按正常或轻微高亮方式显示。遮挡提示层只在目标物与相机之间有更近的不透明遮挡物时把目标物的轮廓或幽灵体叠加到画面上。为了拿到“有没有被遮挡”这个信息最直接的办法是在后处理阶段比较屏幕点的目标物深度和场景深度。对画面中每个目标物像素如果它的深度大于该像素处场景不透明深度说明有物体挡在它前面如果它的深度等于场景不透明深度说明这个像素自己就是离相机最近的那个不透明表面如果它的深度小于场景深度说明相机在前面没有遮挡。一旦判断出被遮挡再加入半透明幽灵颜色或轮廓高亮就能得到“墙后面隐约看到队友轮廓”的提示效果。4.2 用单独通道先记录目标物深度实际操作时我会在正常场景渲染完成后增加一个额外 Render Pass把所有需要做幽灵提示的物体单独画到一张深度或颜色纹理上。这一步不解剖到正式效果的完整代码重点是理清顺序第一步主相机正常渲染场景拿到场景颜色和深度第二步把只包含“目标层”物体的摄像机画面渲染到一张临时 RenderTexture第三步后处理 Shader 同时采样主场景深度、目标物深度第四步根据深度比较结果在被遮挡像素处叠加幽灵颜色第五步把结果合成到最终画面。这个方案的好处是通用也和渲染管线解耦核心都在纹理和深度比较上不依赖某一种特殊 Shader 关键字。缺点是会多一次目标层渲染多余顶点和绘制命令会增加开销。对于角色少、目标层物体少的游戏影响很小如果一次性要对几百个对象做穿透提示就要考虑用实例化渲染或者只在视锥体内帧率较高的目标层执行。4.3 透明和不透明队列的选择幽灵层通常是半透明的需要排在场景最后绘制或者直接通过后处理合成。不能把它放在不透明队列里因为它很可能把半透明色写到 z-buffer 上导致后面的半透明物体排序错误。原则是幽灵提示尽量不写深度、不做阴影投射只作为颜色层叠加。如果幽灵体本身还要被其他场景物件继续遮挡比如墙后又有一根柱子那就需要额外判断更多深度层情况会更复杂。普通项目的实际需求通常不会要求幽灵再去遮挡幽灵所以可以明确接受“最外层遮挡物后面可见”这一层语义。5. 轮廓和幽灵效果合流注意合成顺序5.1 先确定优先级当轮廓描边和幽灵遮挡都启用时要提前决定一个被墙挡住的敌人应该显示“穿透幽灵体”还是“只有一圈轮廓浮在墙面上”还是“幽灵体加轮廓”。优先级不同合成 Shader 的判断顺序不同。我的建议是先做深度遮挡判断如果是被遮挡像素则输出幽灵色和轮廓色合成的结果如果没被遮挡但处在边缘梯度上则输出普通轮廓结果其他像素原样透出场景颜色。这种情况要采用分层结果叠加而不是先描边再叠幽灵。否则在墙和敌人交界的位置描边会先被画一次幽灵层再被画一次两个半透明效果重复叠加颜色会变得很脏。把判断集中在一个 Pass 内完成可以避免多次纹理采样带来的重复加色。5.2 一张“区域遮罩”减少采样次数更进阶的写法是先把整个屏幕的目标状态写进一张遮罩纹理目标物完全可见的区域标为 1目标物被遮挡区域标为 2无目标区域标为 0。然后最终合成 Shader 只采样一次遮罩再根据遮罩值决定输出哪个分支。这样比逐像素同时采样多个深度纹理更省性能也方便后续扩展。比如以后要做“被遮挡 30% 后提示”的渐变效果遮罩里完全可以存放更多信息。5.3 颜色合成时注意 HDR 和线性空间现在的 Unity 项目大多开启线性色彩空间后处理 Shader 的混合结果要符合线性光照。如果你直接给描边颜色写死一个 SRGB 的亮橙色在最终显示时可能会觉得颜色偏暗或偏亮。建议数值调色时以最终相机输出为基准不要只看材质预览窗口。更稳妥的做法是把描边强度、幽灵透明度、边缘光强弱都接到 Volume 参数上这样美术可以在运行时用 Volume 实时调试不需要反复回编辑器改材质。6. 参数调试和常见问题排查6.1 先把“深度纹理是否正常”可视化调这类效果最容易犯的错是不看深度纹理就直接调阈值。正确的第一步是写一个 debug Shader把深度纹理和法线纹理直接输出到屏幕上。判断标准深度纹理中近处物体偏白还是偏黑取决于项目是否使用 Reversed Z但远近关系必须明显法线纹理中不同朝向的表面应该呈现不同颜色半透明物体会自动被忽略因为半透明物体通常不写入深度这是正常现象。如果深度纹理完全是一片黑色或白色说明相机没有开启DepthTextureMode或者没有合适的数据源。这时候去调轮廓阈值没有意义。6.2 幽灵遮挡不显示时优先查三样东西遇到过很多次“目标物明明在墙后面但幽灵效果不出来”原因往往不是 shader 本身而是目标物的 Mesh Renderer 被相机的 Culling Mask 排除掉了幽灵层绘制时 ZWrite 和 ZTest 设置反了目标物在场景里因为剔除已经根本没进入渲染范围。排查顺序建议先在场景激活或帧调试器中确认目标物确实被绘制再确认幽灵层单独渲染到一个 Debug 纹理上时是否包含目标物最后再看合成 Pass。6.3 描边闪烁或断线时先看采样偏移和 MSAA采样偏移越大边缘越粗但断线概率也越高。当目标很小、移动很快时粗描边会非常不稳定。我一般会把基础采样半径控制在 1 到 2 个纹素然后通过二次模糊让边缘更平滑而不是一味调大 Line Width。另外要留意相机是否开启了 MSAA。后处理前如果场景做了 MSAA深度纹理可能需要做 Resolve否则采样到的深度可能在边缘出现抖动。这不是每一台设备都存在问题但移动端尤其容易遇到。遇到描边不稳定时先关掉 MSAA 对比一次能很快定位是否是多采样问题。6.4 性能边界低端机跑一遍后再决定是否批量屏幕空间后处理的性能开销主要在纹理采样上解析度越高每个像素的采样成本越明显。一个最简单的四邻域采样每个像素已经有多次深度和法线读取。如果把采样半径拉到 8 纹素而目标物体又多帧率就可能明显下降。我在落地时一般会做以下分级运行条件建议配置新手学习、PC 或编辑器验证四邻域轮廓Line Width 1 到 2普通动作游戏主目标提示只对目标层生成 mask轮廓采样限制在 mask 区域移动端低配置降低后处理解析度避免使用大半径采样用深度边缘配合一个较窄半径需要几十个目标同时提示先做实例化渲染再考虑贴花式合成不要逐物体生成多个 RenderTexture7. 几个我实际测试时体会比较深的点先把单任务跑通再开批量。这句话放在屏幕空间轮廓里同样适用。如果一个简单的立方体加一面墙都调不出稳定效果直接放进复杂场景只会得到一堆边缘噪声。我在做这类功能时常提醒自己后处理是画满全屏的采样所有项目里超过 1080p 分辨率任何多一次的纹理读取都会被放大到几十万甚至上百万像素。不要为了一个角落里偶尔出现的需求让全屏每个像素都承担高成本。宁可把目标层 mask 先算出来再在 mask 区域执行细节操作。关于幽灵遮挡我的态度是能先用深度比较解决就不用额外造复杂通道。如果只是提示墙后面敌人一个比较柔和的光晕轮廓经常比把整个人体模型都穿透显示出来更合适。穿透显示会让玩家看到完整模型但也会导致场景层次感变差竞技游戏里甚至会造成不公平信息。设计上应该想清楚是“提示位置”还是“展示动作”这两者对视觉强度的需求完全不一样。最后屏幕空间效果的输出受项目基础设置影响远比想象中更大。线性空间、HDR、MSAA、半透明队列、粒子系统、遮挡剔除等任何一个环节变了最后像素结果都可能变。所有参数都不要写死在 Shader 里尽量通过 Material 或 Volume 暴露成可调项方便不同关卡里微调。真正把这类效果做得稳定的项目靠的不是一个惊艳的 shader 文件而是把深度数据、渲染层、队列顺序和调试可视化都理顺了。
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

基于51单片机的LC振荡金属探测器设计与实现 2026/9/4 21:01:56

基于51单片机的LC振荡金属探测器设计与实现

简介:本资源是一套完整的基于52单片机的金属探测器开发套件,面向嵌入式初学者、电子设计爱好者及高校课程实践者,解决金属检测类硬件项目从原理理解到实物落地的学习痛点。压缩包共24个文件,包含核心设计文件:原理图&a…

阅读更多 →
从46%到4%:谷歌Co-Scientist如何用验证流程击穿AI幻觉 2026/9/4 21:01:56

从46%到4%:谷歌Co-Scientist如何用验证流程击穿AI幻觉

最近让我停下手头 prompt 调试的,不是某个新模型发布,而是一组来自 Google 论文的数字:Co-Scientist 加入可靠性模块后,在论文结果类任务上的幻觉率从 46% 降到了 4%。先说明一下背景。Co-Scientist 是 Google 面向科研场景推出的…

阅读更多 →
AMAT 0190-08825干式泵 2026/9/4 21:01:56

AMAT 0190-08825干式泵

AMAT 0190-08825干式泵是一款由丰田生产、应用材料认证使用的干式真空泵,型号为IPUP T100L,专为半导体制造等严苛工业环境设计。产品特点采用无油干式设计,避免油蒸气污染,确保工艺腔体洁净度能够实现高真空度抽气效果&#xff0c…

阅读更多 →
环境对齐与本地云任务接续:云端编码工作流连续性解析 2026/9/4 21:01:56

环境对齐与本地云任务接续:云端编码工作流连续性解析

最近,开发者社区里有一条关于云端编码的帖子被转了不少次,核心议题被概括成“Dex Horthy 转发云端编码实测”。一个有意思的细节是,大家转发时候很少强调原帖的数据有多完整、流程有多严谨,反而都把注意力集中在标题后半截的两个词…

阅读更多 →
写论文避坑指南:用对工具,效率翻倍 2026/9/4 21:01:56

写论文避坑指南:用对工具,效率翻倍

写论文时的那些坑:如何用工具提升效率 作为一名正在进行毕业设计的大学生,我深知在论文写作过程中,常常会在一些重复的环节上耗费大量时间,比如参考文献格式的整理、中英文混排的处理、文本修改的反复和人工核对的繁琐。为了提高…

阅读更多 →
AI Agent失控风险防控:从权限隔离到工作流测试的工程实践 2026/9/4 20:58:56

AI Agent失控风险防控:从权限隔离到工作流测试的工程实践

最近 AI Agent 相关的讨论热度一直不低,尤其在多智能体协作、智能体开发、智能体工作流验证这些方向被反复提起之后,一个更具冲击力的说法也频繁出现:“有顶级预测者给出了 72% 的概率,认为当前已经存在人类不知情的失控 AI 智能体…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞