【URP】Unity[RendererFeatures]渲染对象RenderObjects

【从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通过继承ScriptableRendererFeatureScriptableRenderPass实现管线扩展,核心逻辑在Execute()方法中通过CommandBuffer提交绘制指令。其本质是通过URP的ScriptableRenderContext调度GPU渲染命令,与内置管线不同之处在于采用可编程的轻量级渲染管线架构。

  • 渲染流程控制

    通过RenderPassEvent枚举插入到URP的固定管线阶段(如AfterRenderingOpaques),底层会触发以下操作:

    • 调用ConfigureTarget()设置渲染目标
    • 使用FilteringSettings过滤指定Layer的物体
    • 通过DrawingSettings配置Shader Pass和排序规则
  • 材质替换机制

    当启用Override Material时,URP会临时替换原始材质的Shader,但保留物体的顶点数据。该过程通过MaterialPropertyBlock实现动态参数传递,避免材质实例化开销。

实现示例

  • OutlineFeature.cs

    csharp 复制代码
    using 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

    c 复制代码
    Shader "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

    csharp 复制代码
    using 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

    c 复制代码
    Shader "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开始探索游戏渲染】专栏-直达 (欢迎点赞留言探讨,更多人加入进来能更加完善这个探索的过程,🙏)

相关推荐
UWA5 天前
Gears 实测室:第六期・《黑色沙漠》手游如何精准决策性能优化
性能优化·游戏开发·uwa·游戏测试
用户294655509197 天前
在unity中,如何把敌人和玩家之间的碰撞检测的性能优化一下
游戏开发
陈尕六8 天前
从零开始的 Godot 之旅 — EP10:有限状态机(二)
godot·游戏开发
祭璃妖9 天前
关于2D人物冒险游戏闪现逻辑的实现方法
游戏开发
专注VB编程开发20年13 天前
游戏开发入门,简单小游戏原理-关于2D渲染的一些小想法
小游戏·游戏开发
陈尕六15 天前
从零开始的 Godot 之旅 — EP9:有限状态机(一)
godot·游戏开发