尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

Unity HDRP Custom Pass实现物体高亮选中效果:从原理到实践

Unity HDRP Custom Pass实现物体高亮选中效果:从原理到实践 1. 项目概述为什么我们需要自定义的物体高亮选中效果在Unity HDRP高清渲染管线项目中实现一个清晰、美观且性能可控的物体选中高亮效果是交互体验中至关重要的一环。无论是用于策略游戏的单位框选、RPG游戏的道具交互还是编辑器工具的视觉反馈一个优秀的选中效果能极大地提升产品的专业度和用户的沉浸感。HDRP本身提供了强大的渲染能力但默认的选中高亮方案往往难以满足定制化需求比如我们想要一个边缘发光、颜色叠加或者特定轮廓线的效果。这时HDRP的Custom Pass自定义通道功能就成为了我们的“瑞士军刀”。它允许我们在HDRP的标准渲染管线中的特定注入点Injection Point插入我们自己的渲染逻辑从而在不破坏原有渲染流程的前提下实现高度定制化的视觉效果。而“Selection通道”则是我们实现这一效果的核心思路通过一个独立的渲染通道专门处理被选中物体的渲染实现与场景其他物体的完美隔离和效果叠加。我最近在为一个圣诞主题的模块化风格化场景比如网络上热门的“Modular Stylized Christmas Market Bundle”实现交互功能时就深度使用了这项技术。传统的在材质上叠加发光或改变颜色的方法在面对复杂场景、大量物体或需要后处理效果时往往力不从心。而基于Custom Pass的Selection通道方案不仅效果灵活、性能可控还能轻松实现诸如距离衰减、遮挡处理等高级特性。接下来我将带你从零开始手把手实现一个完整的、基于HDRP Custom Pass的物体高亮选中效果。2. 核心思路与方案设计理解Custom Pass与Selection通道在动手写代码之前我们必须先理清整个方案的设计思路。这决定了我们代码的结构和最终效果的健壮性。2.1 Custom Pass工作流解析HDRP的Custom Pass本质上是一个“钩子”系统。它允许我们在摄像机渲染的特定阶段如不透明物体渲染后、透明物体渲染前等执行我们自定义的绘制命令。整个过程可以概括为以下几个步骤创建Custom Pass Volume这是一个GameObject上的组件它定义了自定义通道生效的范围全局或局部和执行时机注入点。添加具体的Pass在Volume内我们可以添加不同类型的Pass如FullScreenCustomPass全屏后处理或DrawRenderersCustomPass绘制特定渲染器。对于物体高亮我们主要使用后者。编写着色器Shader为DrawRenderersCustomPass指定一个自定义的Shader。这个Shader决定了被选中物体“长什么样”——是发光、描边还是变色。逻辑联动在C#脚本中管理一个“被选中物体列表”并将这个列表动态地传递给Custom Pass Volume或其关联的材质从而控制“画谁”和“怎么画”。2.2 “Selection通道”的设计哲学所谓“Selection通道”并非HDRP内置的一个神秘通道而是一种设计模式。其核心思想是将“选中状态”的渲染与“常规状态”的渲染完全分离。常规渲染通道所有物体按照其原有的材质和着色器进行渲染写入相机的颜色和深度缓冲区。Selection通道在一个靠后的注入点例如在后期处理之前我们再次绘制被选中的物体但使用一套完全不同的、专门用于高亮的着色器。这个通道的渲染结果可以以多种方式与主画面结合叠加Additive将高亮颜色直接加到主画面上实现发光效果。混合Blend以特定的透明度与主画面混合实现颜色覆盖。深度测试Depth Test通过配置深度测试如Equal可以实现完美的轮廓线效果即只绘制未被其他物体遮挡的轮廓部分。这种分离带来的好处是巨大的非侵入式无需修改场景中任何原有物体的材质或着色器。高度可控高亮效果颜色、强度、样式完全由我们自定义的Shader控制与物体原本外观无关。性能优化我们只对选中的少数物体进行二次绘制开销很小。并且可以通过Layer、Queue等条件进行精确的渲染过滤。2.3 注入点Injection Point的选择策略注入点的选择直接影响到高亮效果与其他渲染元素如雾效、后期抗锯齿、运动模糊的交互关系。根据官方文档和我的实践经验对于物体高亮以下几个注入点最为常用After Post-Process在所有后期处理如颜色分级、Bloom、运动模糊之后执行。这意味着你的高亮效果也会被Bloom等效果影响可能会产生非常炫目的“光晕”效果但同时也可能被一些后处理如特定的色调映射改变颜色。适合追求极致发光、希望与后处理深度整合的效果。Before Post-Process在后期处理之前但不透明和透明物体渲染都完成之后执行。这是最常用、最稳妥的选择。你的高亮效果会作为一个独立的图层然后和其他画面元素一起进入后期处理流程。这样可以保证高亮颜色的准确性并且效果稳定。Before Transparent在透明物体渲染之前执行。如果你的高亮物体需要与透明物体如玻璃、粒子有正确的遮挡关系可能需要考虑这个点。但通常高亮作为一层覆盖放在透明物体之后即Before Post-Process更为常见。实操心得对于大多数项目我强烈建议从Before Post-Process开始。它的行为最可预测与HDRP各种特性的兼容性也最好。只有在你有特殊需求比如希望高亮轮廓线不被景深模糊影响时才需要考虑更靠前的注入点如After Opaque Depth And Normal但这会带来更复杂的深度和法线缓冲区处理。3. 环境准备与核心组件创建理论清晰后我们开始动手搭建。首先确保你有一个启用了HDRP的项目。如果是从空项目开始记得通过Package Manager导入High Definition RP并使用HDRP模板创建场景。3.1 创建Custom Pass Volume这是所有自定义渲染的容器。在Hierarchy窗口中右键 -Volume - Custom Pass Volume。Unity会创建一个带有Custom Pass Volume组件的GameObject。选中该GameObject在Inspector中查看Custom Pass Volume组件。Mode选择Global。这样高亮效果将对场景中所有摄像机生效。如果你只想对特定区域如一个小房间内的摄像机生效可以选择Local并添加碰撞体定义范围。Injection Point选择Before Post-Process。这是我们之前讨论的最佳起点。Fade Radius当Mode为Local时有效用于定义效果在体积边界淡入淡出的范围这里我们保持默认。3.2 添加Draw Renderers Custom Pass这是用来绘制我们选中物体的具体通道。在Custom Pass Volume组件的Custom Passes列表下方点击(加号)按钮。从下拉菜单中选择Draw Renderers Custom Pass。列表中会出现一个新的Pass项。3.3 配置Draw Renderers Custom Pass参数这是最关键的一步它决定了“画什么”和“怎么画”。Name给它起个名字比如“SelectionHighlight”。这有助于在性能分析器Profiler或Frame Debugger中识别它。Target Color/Depth Buffer通常都设置为Camera。这意味着我们的绘制结果会直接输出到主摄像机的颜色和深度缓冲区与场景画面混合。如果你想先渲染到一个中间缓冲区做更复杂的处理可以选择Custom但这需要额外配置HDRP Asset中的Custom Buffer Format对于基础高亮来说过于复杂。Clear Flags设置为None。我们不希望清除摄像机缓冲区已有的内容即场景画面我们只想在上面叠加内容。Filters这是过滤渲染对象的核心区域。Queue选择Transparent。因为我们的高亮效果通常需要与背景混合例如使用Alpha混合所以使用透明队列。如果你想做深度测试描边也可以使用Opaque但混合方式需要调整。Layer Mask这里先留空或选择一个你专门用于测试的层如“Ignore Raycast”。我们最终会通过C#脚本来动态控制这个Layer Mask以实现“选中谁就画谁”。OverridesMaterial暂时留空。我们需要先创建一个自定义的高亮着色器然后指定到这里。Pass Name当指定了Material后这里会出现该材质包含的Pass。对于HDRP Lit Shader我们通常选择ForwardOnly来渲染颜色。4. 编写核心高亮着色器Shader着色器是视觉效果的心脏。我们将创建一个专门用于高亮渲染的Unlit Shader。4.1 创建Custom Renderers Pass Shader在Project窗口中右键 -Create - Shader - HDRP - Custom Renderers Pass。将其命名为“SelectionHighlight.shader”。双击打开这个Shader文件。你会看到一个已经包含了基础结构的HDRP自定义着色器模板。4.2 剖析与修改着色器代码模板代码已经为我们搭建了框架但我们需要将其改造成一个简单而强大的高亮着色器。我将逐部分解释并给出修改后的代码。首先在HLSLINCLUDE块和Pass中我们需要定义一些属性Properties比如高亮颜色和强度。// 在 Properties 块中定义 _SelectionColor(Selection Color, Color) (1, 0.5, 0, 1) _Intensity(Intensity, Range(0, 5)) 1.0接下来最关键的是片元着色器Fragment Shader函数GetSurfaceAndBuiltinData。这个函数负责计算每个像素最终输出的颜色。// 这是核心的片元着色器函数 void GetSurfaceAndBuiltinData(FragInputs fragInputs, float3 viewDirection, inout PositionInputs posInput, out SurfaceData surfaceData, out BuiltinData builtinData) { // 1. 初始化 BuiltinData。对于无光照的高亮我们不需要光照数据。 ZERO_INITIALIZE(BuiltinData, builtinData); // 2. 计算基础颜色。这里我们直接使用我们定义的 _SelectionColor。 float3 highlightColor _SelectionColor.rgb * _Intensity; // 3. 可选基于法线与视角方向计算边缘强度实现轮廓光效果。 // 获取顶点法线从物体空间转换到世界空间 float3 worldNormal normalize(fragInputs.tangentToWorld[2].xyz); // 计算法线与视角方向的点积。边缘处法线与视线垂直值接近0正面接近1。 float rim 1.0 - abs(dot(viewDirection, worldNormal)); // 使用平滑步长增强边缘效果 rim smoothstep(0.0, 0.7, rim); // 将边缘因子混合到颜色中 float3 finalColor lerp(highlightColor, highlightColor * 2.0, rim); // 4. 填充输出结构。 // SurfaceData 通常用于光照计算但我们这里是无光照的所以只给一个颜色。 // 注意即使是无光照也需要正确初始化。 surfaceData.color finalColor; // 基础颜色 // 其他SurfaceData参数对于无光照着色器可以保持为0或默认值但为了安全我们显式初始化。 surfaceData.metallic 0; surfaceData.specular 0; surfaceData.smoothness 0; surfaceData.normalWS float3(0, 0, 0); surfaceData.bentNormal float3(0, 0, 0); surfaceData.coatMask 0; surfaceData.ambientOcclusion 1; // 环境光遮蔽设为1表示无遮蔽 surfaceData.specularOcclusion 1; surfaceData.diffusionProfileHash 0; surfaceData.subsurfaceMask 0; surfaceData.thickness 0; surfaceData.ior 1.0; surfaceData.transmittanceColor float3(0,0,0); surfaceData.atDistance 0; surfaceData.transmittanceMask 0; // BuiltinData 包含一些HDRP需要的通用数据。 builtinData.opacity _SelectionColor.a; // 使用颜色的Alpha通道作为整体透明度 builtinData.emissiveColor float3(0, 0, 0); // 我们不是自发光所以为0 // 非常重要将我们的颜色赋值给 builtinData.emissiveColor 可以让它在无光照模式下显示。 // 但同时我们需要告诉HDRP不要应用光照。将 bsdfData 的特征标志位设置为无光照。 // 更简单的方式我们直接输出到 surfaceData.color并在混合模式上做文章。 // 实际上对于 Custom Pass 的 Draw Renderers我们通常使用简单的片元着色器输出不经过复杂的BSDF。 // 因此一个更直接的方法是修改上方的 ShaderLab Pass使用一个简单的 float4 frag() 函数。 }等等你会发现上面的方法有点复杂因为我们在试图适配HDRP的复杂光照数据结构。对于Custom Pass中的DrawRenderers我们通常可以使用一个更简单的、类似传统Unlit Shader的写法。让我们换一种更清晰的实现方式。4.3 简化版高亮着色器实现实际上HDRP的Custom Renderers PassShader模板提供了多种可能性。对于纯色高亮或简单效果我们可以直接修改顶点/片元着色器而不必完全遵循GetSurfaceAndBuiltinData的复杂流程。但为了兼容性和功能强大我们通常还是在其框架内修改。更实用的方法是我们利用builtinData.emissiveColor。在HDRP中自发光颜色是直接叠加到最终画面上的不受光照影响。这完美符合我们的高亮需求。修改后的核心函数如下void GetSurfaceAndBuiltinData(FragInputs fragInputs, float3 viewDirection, inout PositionInputs posInput, out SurfaceData surfaceData, out BuiltinData builtinData) { // 完全不初始化或使用默认的SurfaceData因为我们不依赖表面光照属性。 // 但为了安全我们将其颜色设为黑色。 ZERO_INITIALIZE(SurfaceData, surfaceData); surfaceData.color float3(0, 0, 0); // 初始化BuiltinData ZERO_INITIALIZE(BuiltinData, builtinData); // --- 核心高亮计算 --- float3 worldNormal normalize(fragInputs.tangentToWorld[2].xyz); float rim 1.0 - abs(dot(viewDirection, worldNormal)); rim smoothstep(0.2, 0.8, rim); // 调整边缘检测范围 float3 baseHighlight _SelectionColor.rgb * _Intensity; float3 finalEmission lerp(baseHighlight, baseHighlight * 1.5, rim); // 关键将计算好的颜色赋给自发光输出 builtinData.emissiveColor finalEmission; // 设置不透明度 builtinData.opacity _SelectionColor.a; // 禁用深度写入因为我们只是叠加效果。这通常在Custom Pass的材质覆盖中设置但这里也可以控制。 // builtinData.depthOffset ... 如果需要可以设置深度偏移 // 告诉HDRP这个表面是发光的并且不参与标准光照计算 // 通过设置某些特征标志可以实现但更简单的方法是依赖我们稍后在材质上设置的混合模式。 }这个方法的精髓在于我们欺骗了HDRP的渲染管线。我们提供了一个“表面”但这个表面的漫反射、高光等所有光照属性都是零黑色。然而我们给了它一个非零的emissiveColor自发光颜色。对于大多数渲染队列自发光会直接叠加到画面上。但是为了让这个“欺骗”更有效我们还需要在Shader中关闭一些不必要的计算并确保它使用正确的混合模式。这通常通过修改ShaderLab的Pass块和Blend状态来实现。不过在HDRP的Custom Renderers Pass模板中这些状态有时由HDRP宏管理。一个更稳妥的方法是我们创建一个基于HDRP/Unlit的Shader因为它更简单。然后通过Custom Pass Volume的Material Override功能强制使用这个Unlit Shader来绘制物体并设置Blend One One相加混合来实现发光叠加效果。考虑到教程的清晰度和成功率我推荐第二种简化方案创建一个标准的HDRP Unlit Shader然后在Custom Pass中覆盖材质。4.4 创建并配置HDRP Unlit高亮材质创建Unlit Shader右键 -Create - Shader - HDRP - Unlit。命名为“HDRP_Unlit_Highlight.shader”。编写简单的片元着色器打开该Shader找到片元着色器部分。它的结构比Custom Renderers Pass简单得多。float4 frag(PackedVaryingsToPS input) : SV_Target { UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(input); float3 positionRWS input.positionRWS; float3 normalWS input.normalWS; float3 viewDirectionWS GetWorldSpaceNormalizeViewDir(positionRWS); // 计算边缘光 float rim 1.0 - saturate(dot(normalize(normalWS), viewDirectionWS)); rim smoothstep(_RimMin, _RimMax, rim); // 使用属性控制边缘范围 // 基础颜色与边缘混合 float3 finalColor _BaseColor.rgb * _BaseColor.a; // 使用BaseColor和其Alpha作为强度 finalColor rim * _RimColor.rgb * _RimColor.a; // 输出颜色使用相加混合需要在材质中设置 return float4(finalColor, 1.0); }你需要在Properties块中添加_BaseColor,_RimColor,_RimMin,_RimMax等属性。创建材质右键该Shader -Create - Material。命名为“Mat_SelectionHighlight”。配置材质在材质的Surface Options中将Surface Type设置为Transparent。在Transparent Depth Prepass和Transparent Depth Postpass中根据是否需要深度写入进行选择。对于叠加效果通常都关闭。在Blending Mode中选择Additive。这是实现发光叠加效果的关键。你也可以选择Alpha混合但Additive的发光感更强。设置你喜欢的颜色和边缘参数。链接到Custom Pass回到之前创建的DrawRenderersCustomPass组件在Overrides - Material字段中拖入我们刚刚创建的“Mat_SelectionHighlight”材质。在Pass Name中选择ForwardOnly。注意事项使用HDRP/UnlitShader并覆盖材质的方法其渲染顺序和深度测试行为可能与你直接在Custom Renderers PassShader中精细控制有所不同。但对于大多数高亮需求纯色叠加、简单边缘光这种方法更简单、更稳定也更容易调整视觉效果。如果遇到深度问题如高亮穿透墙壁可以在材质或Custom Pass的Override Depth设置中调整深度测试Depth Test为Less Equal或Equal。5. C#脚本驱动动态控制选中状态现在我们有了一套渲染高亮的“画笔”和“画布”Custom Pass接下来需要一个“大脑”来告诉系统“画谁”。5.1 创建SelectionManager脚本这个脚本负责管理所有可选中物体并维护一个当前被选中的物体列表。using System.Collections.Generic; using UnityEngine; using UnityEngine.Rendering.HighDefinition; public class SelectionManager : MonoBehaviour { public static SelectionManager Instance; // 单例模式便于访问 // 用于高亮渲染的Layer建议在项目中提前创建好例如名为“Highlight”的第8层 public LayerMask highlightLayer; // 存储当前被选中的物体 private HashSetGameObject _selectedObjects new HashSetGameObject(); // 引用到我们的Custom Pass Volume private CustomPassVolume _customPassVolume; private DrawRenderersCustomPass _highlightPass; void Awake() { if (Instance ! null Instance ! this) { Destroy(this.gameObject); } else { Instance this; } // 寻找场景中的Custom Pass Volume也可以通过Inspector赋值 _customPassVolume FindObjectOfTypeCustomPassVolume(); if (_customPassVolume ! null) { // 找到我们之前创建的名为“SelectionHighlight”的Pass foreach (var pass in _customPassVolume.customPasses) { if (pass.name SelectionHighlight) { _highlightPass pass as DrawRenderersCustomPass; break; } } } if (_highlightPass null) { Debug.LogError(SelectionHighlight DrawRenderersCustomPass not found! Please check the name in the Custom Pass Volume.); } } // 选中一个物体 public void SelectObject(GameObject obj) { if (obj null) return; if (_selectedObjects.Add(obj)) { // 将该物体移动到高亮Layer obj.layer (int)Mathf.Log(highlightLayer.value, 2); // 将LayerMask转换为index UpdateCustomPassFilter(); } } // 取消选中一个物体 public void DeselectObject(GameObject obj) { if (obj null) return; if (_selectedObjects.Remove(obj)) { // 将物体移回其原始Layer这里需要你记录原始Layer简化处理移回默认层 obj.layer 0; // 默认层 UpdateCustomPassFilter(); } } // 清空所有选中 public void ClearSelection() { foreach (var obj in _selectedObjects) { if (obj ! null) obj.layer 0; } _selectedObjects.Clear(); UpdateCustomPassFilter(); } // 更新Custom Pass的Layer Mask过滤器 private void UpdateCustomPassFilter() { if (_highlightPass ! null) { // 设置Custom Pass只渲染我们指定的高亮层 _highlightPass.layerMask highlightLayer; } } // 示例通过射线检测进行点击选中 void Update() { if (Input.GetMouseButtonDown(0)) { Ray ray Camera.main.ScreenPointToRay(Input.mousePosition); RaycastHit hit; if (Physics.Raycast(ray, out hit)) { // 假设点击到的物体就是要选中的 SelectObject(hit.collider.gameObject); } } if (Input.GetMouseButtonDown(1)) { // 右键清空选择 ClearSelection(); } } }5.2 创建SelectableObject脚本可选但推荐为了更优雅地管理物体的选中状态比如记录原始Layer、处理带有子渲染器的物体等可以给每个可选中物体附加一个脚本。using UnityEngine; public class SelectableObject : MonoBehaviour { private int _originalLayer; private Renderer[] _renderers; void Start() { _originalLayer gameObject.layer; _renderers GetComponentsInChildrenRenderer(); } public void SetHighlighted(bool isHighlighted) { int targetLayer isHighlighted ? SelectionManager.Instance.highlightLayer.value : _originalLayer; // 需要将LayerMask转换为index这里假设highlightLayer是单个层 int layerIndex (int)Mathf.Log(targetLayer, 2); gameObject.layer layerIndex; // 如果物体有子物体也需要递归设置这里简化处理 // foreach(var rend in _renderers) { rend.gameObject.layer layerIndex; } } void OnDestroy() { // 如果物体被销毁时仍被选中从管理器中移除 // 这部分逻辑可以放在SelectionManager中通过事件通知 } }然后修改SelectionManager的SelectObject和DeselectObject方法调用SelectableObject的SetHighlighted方法。5.3 最终组装与测试在场景中创建一个空物体挂载SelectionManager脚本。在Inspector中将Highlight Layer设置为你在项目中创建好的专用层例如“Highlight”的第8层。确保你的Custom Pass Volume中DrawRenderersCustomPass的Layer Mask也设置为同一个“Highlight”层。这样只有当物体被选中并切换到该层时才会被高亮通道渲染。给场景中的一些物体比如Cube、Sphere添加SelectableObject脚本或者直接用SelectionManager的射线检测进行测试。运行游戏。点击物体你应该能看到它被渲染上我们自定义的高亮材质比如发光效果。右键点击可以清空选择。6. 高级技巧与性能优化基础功能实现后我们可以进一步打磨效果和性能。6.1 实现更精致的轮廓线效果之前的边缘光Rim Light是一种模拟轮廓的方法。另一种更精确的轮廓线效果是使用“法线扩展”技术。这需要在顶点着色器中沿着顶点法线方向将顶点位置向外稍微挤出。在你的HDRP_Unlit_Highlight着色器中修改顶点着色器PackedVaryingsToPS vert(AttributesMesh input) { VaryingsToPS output (VaryingsToPS)0; // 将顶点位置转换到世界空间 float3 positionWS TransformObjectToWorld(input.positionOS.xyz); // 获取世界空间法线 float3 normalWS TransformObjectToWorldNormal(input.normalOS); // 沿着法线方向挤出顶点轮廓线宽度 positionWS normalWS * _OutlineWidth; // 将挤出后的位置转换回裁剪空间 output.positionCS TransformWorldToHClip(positionWS); // 传递其他数据如法线、UV等 output.positionRWS positionWS; output.normalWS normalWS; // ... 传递其他必要的变量 return PackVaryingsToPS(output); }然后在片元着色器中直接输出一个固定的轮廓颜色。同时你需要两个Pass一个Pass用于绘制向后挤出的轮廓通常设置为深度测试Less并关闭深度写入ZWrite Off混合模式为Blend SrcAlpha OneMinusSrcAlpha另一个Pass用于绘制物体本身的发光填充。这比单一Pass的边缘光计算量稍大但轮廓更准确。6.2 多摄像机与局部Volume支持如果你的游戏有分屏、画中画或者多个功能摄像机如小地图摄像机你可能不希望高亮效果出现在所有摄像机上。解决方案将Custom Pass Volume的Mode设置为Local并为其添加一个Collider如Box Collider来定义作用范围。将该Volume放置在只应显示高亮的主摄像机附近。对于其他摄像机可以创建另一个Mode为Global但Enabled为false的Volume或者通过脚本动态启用/禁用不同的Volume。6.3 性能优化要点Layer Mask过滤这是最重要的优化。确保高亮层只包含当前被选中的物体避免渲染无关物体。着色器复杂度高亮着色器应尽可能简单。避免复杂的纹理采样、光照计算或屏幕空间效果。我们的HDRP/Unlit着色器就是一个很好的选择。批处理中断Custom Pass的绘制可能会打断Unity的动态批处理/静态批处理。如果选中的是大量小物体性能开销会增大。对于大量物体的选中如RTS游戏框选一群单位可以考虑使用一个全屏的后处理效果来替代逐物体绘制。例如将选中物体的屏幕空间ID渲染到一个临时缓冲区然后在全屏Custom Pass中根据ID图来施加高亮效果。这更复杂但批处理友好。GPU Instancing确保你的高亮材质启用了Enable GPU Instancing。如果所有选中物体使用同一个材质Unity可以尝试对它们进行实例化渲染提升性能。6.4 常见问题排查高亮效果不显示检查层Layer确认SelectionManager设置的highlightLayer与DrawRenderersCustomPass中的Layer Mask完全一致。使用Layer的索引值或名称进行比对。检查注入点确认Custom Pass的Injection Point设置正确如Before Post-Process。如果放在太靠前的位置如Before Rendering可能会被后续的不透明物体覆盖。检查材质混合模式高亮材质必须是Transparent表面类型并使用Additive或Alpha Blend混合模式。如果混合模式是Opaque它将完全覆盖后面的像素。检查摄像机裁剪确保被选中的物体在摄像机视锥体内。使用Frame Debugger这是最强大的调试工具。打开Window - Analysis - Frame Debugger运行游戏在选中物体后暂停查看渲染事件列表。找到你的Custom Pass名称是你设置的Name点开查看它绘制了哪些物体以及使用的着色器状态。可以直观地看到Draw Call是否被调用以及渲染结果。高亮效果穿透墙壁或物体调整深度测试在DrawRenderersCustomPass组件中展开Overrides启用Override Depth将Depth Test设置为Less Equal。这确保只有当前深度小于等于缓冲区深度即未被遮挡的像素才会被绘制。对于轮廓线效果有时需要设置为Greater来绘制被遮挡的部分。检查材质深度写入确保高亮材质的Depth Write是关闭的Off除非你有特殊需求。深度写入会污染深度缓冲区影响后续渲染。高亮效果闪烁或抖动深度测试与透明排序透明物体之间的渲染顺序可能导致闪烁。尝试在Custom Pass中调整Sorting选项比如改为Back to Front或Front to Back看是否能稳定。抗锯齿MSAA/TAA影响某些后处理抗锯齿如TAA与动态的、每帧变化的叠加层如高亮结合时可能产生闪烁或鬼影。可以尝试在摄像机设置中临时关闭抗锯齿或者将注入点调整到抗锯齿之后如果可能。性能突然下降检查选中物体数量是否一次性选中了成百上千个物体Custom Pass是逐物体绘制的数量过多必然导致性能问题。考虑使用上文提到的基于ID图的全屏后处理方案。使用Profiler打开Window - Analysis - Profiler查看GPU时间。找到Render.Custom或类似条目查看其耗时。同时检查CPU端管理选中列表的脚本是否有性能瓶颈如每帧在Update中做复杂的查找。这套基于HDRP Custom Pass和Selection通道的物体高亮方案我已经在多个商业和原型项目中成功应用。它剥离了效果与逻辑提供了电影级的视觉表现力同时保持了代码的清晰和可维护性。最关键的是理解管线、着色器、状态管理的协作关系一旦掌握了这个模式你就能轻松实现各种自定义的屏幕空间效果远远不止于物体高亮。
返回列表