【从UnityURP开始探索游戏渲染】专栏-直达
RenderObjects的定义与作用
RenderObjects是URP提供的RendererFeature之一,允许开发者在不编写代码的情况下对渲染管线进行定制。它通过配置参数实现选择性渲染特定层级的物体、控制渲染顺序、重载材质或渲染状态等功能57。其核心用途包括:
- 层级过滤:仅渲染指定LayerMask的物体
- 渲染时机控制:通过Event参数插入到渲染管线的不同阶段(如AfterRenderingOpaques)
- 材质替换:使用Override Material覆盖原有材质
- 多Pass渲染:配合Shader的LightMode标签实现描边等效果
发展历史
- 初始版本(2020年前)作为LWRP实验性功能引入
- 2020年URP 7.x版本正式集成,提供基础层过滤和材质替换
- 2021年后增强深度/模板控制,支持透明物体处理
- 2022年优化API结构,明确ScriptableRendererFeature与RenderPass的分离
原理
底层原理
-
架构层级
RenderObjects通过继承
ScriptableRendererFeature和ScriptableRenderPass实现管线扩展,核心逻辑在Execute()方法中通过CommandBuffer提交绘制指令。其本质是通过URP的ScriptableRenderContext调度GPU渲染命令,与内置管线不同之处在于采用可编程的轻量级渲染管线架构。 -
渲染流程控制
通过
RenderPassEvent枚举插入到URP的固定管线阶段(如AfterRenderingOpaques),底层会触发以下操作:- 调用
ConfigureTarget()设置渲染目标 - 使用
FilteringSettings过滤指定Layer的物体 - 通过
DrawingSettings配置Shader Pass和排序规则
- 调用
-
材质替换机制
当启用Override Material时,URP会临时替换原始材质的Shader,但保留物体的顶点数据。该过程通过
MaterialPropertyBlock实现动态参数传递,避免材质实例化开销。
实现示例
-
OutlineFeature.cs
csharpusing UnityEngine; using UnityEngine.Rendering; using UnityEngine.Rendering.Universal; public class OutlineFeature : ScriptableRendererFeature { class OutlinePass : ScriptableRenderPass { private Material _outlineMat; private LayerMask _layerMask; private FilteringSettings _filteringSettings; public OutlinePass(Material mat, LayerMask mask) { _outlineMat = mat; _layerMask = mask; _filteringSettings = new FilteringSettings(RenderQueueRange.opaque, _layerMask); renderPassEvent = RenderPassEvent.AfterRenderingOpaques; } public override void Execute(ScriptableRenderContext context, ref RenderingData data) { var drawingSettings = CreateDrawingSettings( new ShaderTagId("UniversalForward"), ref data, SortingCriteria.CommonOpaque ); drawingSettings.overrideMaterial = _outlineMat; context.DrawRenderers(data.cullResults, ref drawingSettings, ref _filteringSettings); } } [SerializeField] private Material _outlineMaterial; [SerializeField] private LayerMask _outlineLayers = 1; private OutlinePass _pass; public override void Create() => _pass = new OutlinePass(_outlineMaterial, _outlineLayers); public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData data) => renderer.EnqueuePass(_pass); } -
Outline.shader
cShader "Custom/Outline" { Properties { _OutlineColor("Color", Color) = (1,0,0,1) _OutlineWidth("Width", Range(0,0.1)) = 0.03 } SubShader { Tags { "RenderType"="Opaque" "Queue"="Geometry+100" } Pass { Cull Front ZWrite Off CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" float _OutlineWidth; fixed4 _OutlineColor; struct appdata { float4 vertex : POSITION; float3 normal : NORMAL; }; v2f vert(appdata v) { v2f o; v.vertex.xyz += v.normal * _OutlineWidth; o.pos = UnityObjectToClipPos(v.vertex); return o; } fixed4 frag(v2f i) : SV_Target { return _OutlineColor; } ENDCG } } }
关键流程解析
-
渲染指令提交
DrawRenderers方法内部会构建BatchRendererGroup,将CPU侧的渲染数据批量提交至GPU,相比直接使用CommandBuffer更高效。 -
深度测试控制
示例中
ZWrite Off禁用深度写入,使描边始终显示在原始物体表面,该技术也常用于解决透明物体渲染顺序问题。 -
多Pass协作
URP会先执行默认的Forward渲染Pass,再执行RenderObjects插入的Pass,通过
RenderPassEvent控制执行顺序
完整实现流程示例
-
OutlineFeature.cs
csharpusing UnityEngine; using UnityEngine.Rendering; using UnityEngine.Rendering.Universal; public class OutlineFeature : ScriptableRendererFeature { class OutlinePass : ScriptableRenderPass { private Material outlineMat; private LayerMask layerMask; private RenderTargetIdentifier source; public OutlinePass(Material mat, LayerMask mask) { outlineMat = mat; layerMask = mask; renderPassEvent = RenderPassEvent.AfterRenderingOpaques; } public override void Execute(ScriptableRenderContext context, ref RenderingData data) { CommandBuffer cmd = CommandBufferPool.Get("OutlinePass"); var drawSettings = CreateDrawingSettings( new ShaderTagId("UniversalForward"), ref data, SortingCriteria.CommonOpaque); var filterSettings = new FilteringSettings(RenderQueueRange.opaque, layerMask); context.DrawRenderers(data.cullResults, ref drawSettings, ref filterSettings); CommandBufferPool.Release(cmd); } } [SerializeField] private Material outlineMaterial; [SerializeField] private LayerMask outlineLayers; private OutlinePass pass; public override void Create() { pass = new OutlinePass(outlineMaterial, outlineLayers); } public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData data) { renderer.EnqueuePass(pass); } } -
Outline.shader
cShader "Custom/Outline" { Properties { _OutlineColor("Color", Color) = (1,1,1,1) _OutlineWidth("Width", Range(0,0.1)) = 0.05 } SubShader { Tags { "RenderType"="Opaque" "LightMode"="UniversalForward" } Pass { CGPROGRAM // Vertex expansion logic... ENDCG } } }
参数详解与用例
| 参数 | 说明 | 应用场景 |
|---|---|---|
| Event | 渲染时机(如BeforeRenderingPostProcessing) | 控制特效叠加顺序 |
| LayerMask | 目标渲染层级 | 仅对敌人/UI层描边 |
| Override Material | 替换材质 | 角色进入阴影区切换材质 |
| Depth Test | 深度测试模式 | 解决透明物体遮挡问题 |
| Shader Passes | 匹配的Shader LightMode标签 | 多Pass渲染(如"UniversalForward") |
配置步骤
- 创建URP Asset并启用Renderer Features
- 添加RenderObjects Feature到Forward Renderer
- 配置Event为AfterRenderingOpaques(不透明物体)或AfterRenderingTransparents(透明物体)
- 指定目标Layer和替换材质
- 调整Depth/Stencil参数解决遮挡问题
典型应用包括:角色描边、场景分块渲染、特殊效果叠加(如受伤高亮)等。通过组合不同Event和LayerMask可实现复杂的渲染管线控制
【从UnityURP开始探索游戏渲染】专栏-直达 (欢迎点赞留言探讨,更多人加入进来能更加完善这个探索的过程,🙏)