docs.unity3d.com
Search Results for

    Show / Hide Table of Contents

    完整的 Scriptable Renderer Feature 示例

    本节介绍如何为 URP 渲染器创建一个完整的 Scriptable Renderer Feature。

    本示例包含以下部分:

    • 完整的 Scriptable Renderer Feature 示例
      • 示例实现概述
      • 创建示例场景和 GameObject
      • 创建 Scriptable Renderer Feature 并添加到 Universal Renderer
        • 将 Renderer Feature 添加到 Universal Renderer 资源
      • 创建 Scriptable Render Pass
      • 实现自定义渲染 Pass 的设置
      • 在自定义 Renderer Feature 中加入渲染 Pass
      • 实现 Volume 组件
      • 完整示例代码
        • 自定义 Renderer Feature 代码
        • 自定义 Render Pass 代码
        • Volume 组件代码
      • 模糊效果的自定义 Shader

    示例实现概述

    本示例实现一个自定义 Renderer Feature,使用 自定义 Render Pass 为相机输出添加模糊效果。

    实现包含以下部分:

    • 一个 ScriptableRendererFeature 实例,每帧入队一个 ScriptableRenderPass 实例。
    • 一个 ScriptableRenderPass 实例,执行以下步骤:
      • 使用 RenderTextureDescriptor API 创建临时渲染纹理。
      • 通过 RTHandle 和 Blit API 对相机输出应用两次 自定义 Shader 进行模糊处理。

    创建示例场景和 GameObject

    按照以下步骤设置项目:

    1. 创建一个新场景。
    2. 创建两个 GameObject:一个名为 Cube 的立方体和一个名为 Sphere 的球体。
    3. 创建两个材质,并使用 Universal Render Pipeline/Lit Shader。分别命名为 Blue 和 Red,将其基础颜色设置为蓝色和红色。
    4. 将 Red 材质赋予立方体,将 Blue 材质赋予球体。
    5. 调整相机位置,使其能同时看到立方体和球体。

    示例场景应如下图所示:

    示例场景

    创建 Scriptable Renderer Feature 并添加到 Universal Renderer

    1. 创建一个新的 C# 脚本,命名为 BlurRendererFeature.cs。

    2. 在脚本中删除 Unity 自动生成的代码。

    3. 添加以下 using 指令:

      using UnityEngine.Rendering.Universal;
      
    4. 创建 BlurRendererFeature 类,并继承 ScriptableRendererFeature:

      public class BlurRendererFeature : ScriptableRendererFeature    
      
    5. 在 BlurRendererFeature 类中实现以下方法:

      • Create:Unity 在以下情况下调用:

        • 渲染器特性首次加载时。
        • 启用或禁用渲染器特性时。
        • 在 Inspector 面板中更改 Renderer Feature 的属性时。
      • AddRenderPasses:Unity 每帧为每个相机调用该方法,用于向 Scriptable Renderer 注入 ScriptableRenderPass 实例。

    完整代码如下:

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using UnityEngine.Rendering.Universal;
    
    public class BlurRendererFeature : ScriptableRendererFeature
    {
        public override void Create()
        {
    
        }
    
        public override void AddRenderPasses(ScriptableRenderer renderer,
            ref RenderingData renderingData)
        {
    
        }
    }
    

    将 Renderer Feature 添加到 Universal Renderer 资源

    将创建的 Renderer Feature 添加到 Universal Renderer 资源。详细信息请参考 如何向 Renderer 添加 Renderer Feature。

    创建 Scriptable Render Pass

    本节展示如何创建 Scriptable Render Pass 并将其实例加入 Scriptable Renderer 队列。

    1. 创建一个新的 C# 脚本,命名为 BlurRenderPass.cs。

    2. 在脚本中删除 Unity 自动生成的代码,并添加以下 using 指令:

      using UnityEngine.Rendering;
      using UnityEngine.Rendering.Universal;
      
    3. 创建 BlurRenderPass 类,并继承 ScriptableRenderPass:

      public class BlurRenderPass : ScriptableRenderPass
      
    4. 在类中实现 Execute 方法。Unity 每帧为每个相机调用该方法,用于实现 Scriptable Render Pass 的渲染逻辑。

      public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
      { }
      

    完整代码如下:

    using UnityEngine.Rendering;
    using UnityEngine.Rendering.Universal;
    
    public class BlurRenderPass : ScriptableRenderPass
    {
        public override void Execute(ScriptableRenderContext context,
            ref RenderingData renderingData)
        {
            
        }
    }
    

    实现自定义渲染 Pass 的设置

    本节介绍如何实现自定义模糊渲染 Pass 的设置。

    1. 本示例的 Renderer Feature 使用 自定义 Shader,其中一个 Pass 处理水平方向模糊,另一个 Pass 处理垂直方向模糊。为允许用户控制每个 Pass 的模糊强度,在 BlurRendererFeature.cs 脚本中添加以下 BlurSettings 类:

      [Serializable]
      public class BlurSettings
      {
          [Range(0,0.4f)] public float horizontalBlur;
          [Range(0,0.4f)] public float verticalBlur;
      }
      
    2. 在 BlurRendererFeature 类中声明以下字段:

      [SerializeField] private BlurSettings settings;
      [SerializeField] private Shader shader;
      private Material material;
      private BlurRenderPass blurRenderPass;
      
    3. 在 BlurRenderPass 类中添加用于存储设置、材质的字段,并创建使用这些字段的构造函数:

      private BlurSettings defaultSettings;
      private Material material;
      
      public BlurRenderPass(Material material, BlurSettings defaultSettings)
      {
          this.material = material;
          this.defaultSettings = defaultSettings;        
      }
      
    4. 在 BlurRenderPass 类中添加 RenderTextureDescriptor 字段,并在构造函数中初始化它:

      private RenderTextureDescriptor blurTextureDescriptor;
      
      public BlurRenderPass(Material material, BlurSettings defaultSettings)
      {
          this.material = material;
          this.defaultSettings = defaultSettings;
      
          blurTextureDescriptor = new RenderTextureDescriptor(Screen.width,
              Screen.height, RenderTextureFormat.Default, 0);
      }
      
    5. 在 BlurRenderPass 类中声明 RTHandle 字段以存储临时模糊纹理的引用:

      private RTHandle blurTextureHandle;
      
    6. 在 BlurRenderPass 类中实现 Configure 方法。Unity 在执行渲染 Pass 之前调用该方法。

      public override void Configure(CommandBuffer cmd,
          RenderTextureDescriptor cameraTextureDescriptor)
      {
          // 使模糊纹理大小与相机目标大小相同
          blurTextureDescriptor.width = cameraTextureDescriptor.width;
          blurTextureDescriptor.height = cameraTextureDescriptor.height;
      
          // 检查描述符是否发生变化,并在必要时重新分配 RTHandle
          RenderingUtils.ReAllocateIfNeeded(ref blurTextureHandle, blurTextureDescriptor);
      }
      
    7. 在 BlurRenderPass 类中实现 UpdateBlurSettings 方法以更新 Shader 参数。

      private static readonly int horizontalBlurId =
          Shader.PropertyToID("_HorizontalBlur");
      private static readonly int verticalBlurId =
          Shader.PropertyToID("_VerticalBlur");
      
      private void UpdateBlurSettings()
      {
          if (material == null) return;
      
          material.SetFloat(horizontalBlurId, defaultSettings.horizontalBlur);
          material.SetFloat(verticalBlurId, defaultSettings.verticalBlur);
      }
      
    8. 在 Execute 方法中调用 UpdateBlurSettings 并使用 Blit 方法执行模糊效果:

      public override void Execute(ScriptableRenderContext context,
          ref RenderingData renderingData)
      {
          // 从池中获取 CommandBuffer
          CommandBuffer cmd = CommandBufferPool.Get();
      
          RTHandle cameraTargetHandle =
              renderingData.cameraData.renderer.cameraColorTargetHandle;
      
          UpdateBlurSettings();
      
          // 先使用第一个 Shader Pass 进行水平模糊
          Blit(cmd, cameraTargetHandle, blurTextureHandle, material, 0);
          // 再使用第二个 Shader Pass 进行垂直模糊
          Blit(cmd, blurTextureHandle, cameraTargetHandle, material, 1);
      
          // 执行命令缓冲区并释放回池
          context.ExecuteCommandBuffer(cmd);
          CommandBufferPool.Release(cmd);
      }
      
    9. 实现 Dispose 方法,在渲染 Pass 执行完成后销毁材质和临时渲染纹理:

      public void Dispose()
      {
          #if UNITY_EDITOR
                  if (EditorApplication.isPlaying)
                  {
                      Object.Destroy(material);
                  }
                  else
                  {
                      Object.DestroyImmediate(material);
                  }
          #else
                  Object.Destroy(material);
          #endif
      
          if (blurTextureHandle != null) blurTextureHandle.Release();
      }
      

    完整代码请参考 自定义 Render Pass 代码。

    在自定义 Renderer Feature 中加入渲染 Pass

    本节介绍如何在 BlurRendererFeature 类的 Create 方法中实例化 BlurRenderPass,并在 AddRenderPasses 方法中将其加入队列。

    1. 在 BlurRendererFeature 类的 Create 方法中实例化 BlurRenderPass:

      public override void Create()
      {
          if (shader == null)
          {
              return;
          }
          material = new Material(shader);
          blurRenderPass = new BlurRenderPass(material, settings);
      
          renderPassEvent = RenderPassEvent.AfterRenderingSkybox;
      }
      
    2. 在 AddRenderPasses 方法中,将渲染 Pass 加入队列:

      public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData)
      {
          if (renderingData.cameraData.cameraType == CameraType.Game)
          {
              renderer.EnqueuePass(blurRenderPass);
          }
      }
      
    3. 实现 Dispose 方法以销毁 Renderer Feature 创建的材质实例,并调用 blurRenderPass.Dispose() 方法释放资源:

      protected override void Dispose(bool disposing)
      {
          blurRenderPass.Dispose();
          #if UNITY_EDITOR
              if (EditorApplication.isPlaying)
              {
                  Destroy(material);
              }
              else
              {
                  DestroyImmediate(material);
              }
          #else
                  Destroy(material);
          #endif
      }
      

    完整代码请参考 自定义 Renderer Feature 代码。

    实现 Volume 组件

    本节介绍如何实现 Volume 组件,以便在渲染器特性中动态控制模糊参数。

    1. 创建一个新的 C# 脚本,命名为 CustomVolumeComponent.cs。

    2. 继承 VolumeComponent 类,并添加 [Serializable] 属性:

      using System;
      using UnityEngine.Rendering;
      
      [Serializable]
      public class CustomVolumeComponent : VolumeComponent
      {
      
      }
      
    3. 添加 BoolParameter 字段以控制是否启用自定义渲染器特性:

      public class BlurVolumeComponent : VolumeComponent
      {
          public BoolParameter isActive = new BoolParameter(true);
      }
      
    4. 添加模糊参数,以允许在 Volume 组件中控制模糊强度:

      [Serializable]
      public class CustomVolumeComponent : VolumeComponent
      {
          public BoolParameter isActive = new BoolParameter(true);
          public ClampedFloatParameter horizontalBlur =
              new ClampedFloatParameter(0.05f, 0, 0.5f);
          public ClampedFloatParameter verticalBlur =
              new ClampedFloatParameter(0.05f, 0, 0.5f);
      }
      
    5. 修改 BlurRenderPass 类的 UpdateBlurSettings 方法,使其优先使用 Volume 设置,如果没有设置,则使用默认值:

      private void UpdateBlurSettings()
      {
          if (material == null) return;
      
          // 获取 Volume 设置,或使用默认值
          var volumeComponent =
              VolumeManager.instance.stack.GetComponent<CustomVolumeComponent>();
          float horizontalBlur = volumeComponent.horizontalBlur.overrideState ?
              volumeComponent.horizontalBlur.value : defaultSettings.horizontalBlur;
          float verticalBlur = volumeComponent.verticalBlur.overrideState ?
              volumeComponent.verticalBlur.value : defaultSettings.verticalBlur;
          material.SetFloat(horizontalBlurId, horizontalBlur);
          material.SetFloat(verticalBlurId, verticalBlur);
      }
      
    6. 在 Unity 场景中创建 局部 Box Volume。如果缺少 Volume Profile,点击 New 创建,并添加 Custom Volume Component Override。

      Box Volume 属性

    7. 启用 Custom Volume Component 设置,并调整数值。将 Volume 位置调整到相机内部,以确保生效。

    完整示例代码

    本节包含示例中所有脚本的完整代码。

    自定义 Renderer Feature 代码

    以下是自定义 Renderer Feature 脚本的完整代码。

    using System;
    using UnityEditor;
    using UnityEngine;
    using UnityEngine.Rendering.Universal;
    
    public class BlurRendererFeature : ScriptableRendererFeature
    {
        [SerializeField] private BlurSettings settings;
        [SerializeField] private Shader shader;
        private Material material;
        private BlurRenderPass blurRenderPass;
    
        public override void Create()
        {
            if (shader == null)
            {
                return;
            }
            material = new Material(shader);
            blurRenderPass = new BlurRenderPass(material, settings);
            
            blurRenderPass.renderPassEvent = RenderPassEvent.AfterRenderingSkybox;
        }
    
        public override void AddRenderPasses(ScriptableRenderer renderer,
            ref RenderingData renderingData)
        {
            if (renderingData.cameraData.cameraType == CameraType.Game)
            {
                renderer.EnqueuePass(blurRenderPass);
            }
        }
    
        protected override void Dispose(bool disposing)
        {
            blurRenderPass.Dispose();
            #if UNITY_EDITOR
                if (EditorApplication.isPlaying)
                {
                    Destroy(material);
                }
                else
                {
                    DestroyImmediate(material);
                }
            #else
                    Destroy(material);
            #endif
        }
    }
    
    [Serializable]
    public class BlurSettings
    {
        [Range(0, 0.4f)] public float horizontalBlur;
        [Range(0, 0.4f)] public float verticalBlur;
    }
    

    自定义 Render Pass 代码

    以下是自定义 Render Pass 脚本的完整代码。

    using UnityEditor;
    using UnityEngine;
    using UnityEngine.Rendering;
    using UnityEngine.Rendering.Universal;
    
    public class BlurRenderPass : ScriptableRenderPass
    {
        private static readonly int horizontalBlurId =
            Shader.PropertyToID("_HorizontalBlur");
        private static readonly int verticalBlurId =
            Shader.PropertyToID("_VerticalBlur");
    
        private BlurSettings defaultSettings;
        private Material material;
    
        private RenderTextureDescriptor blurTextureDescriptor;
        private RTHandle blurTextureHandle;
    
        public BlurRenderPass(Material material, BlurSettings defaultSettings)
        {
            this.material = material;
            this.defaultSettings = defaultSettings;
    
            blurTextureDescriptor = new RenderTextureDescriptor(Screen.width,
                Screen.height, RenderTextureFormat.Default, 0);
        }
    
        public override void Configure(CommandBuffer cmd,
            RenderTextureDescriptor cameraTextureDescriptor)
        {
            // 设置模糊纹理大小,使其与相机目标大小相同
            blurTextureDescriptor.width = cameraTextureDescriptor.width;
            blurTextureDescriptor.height = cameraTextureDescriptor.height;
    
            // 检查描述符是否更改,如有必要重新分配 RTHandle
            RenderingUtils.ReAllocateIfNeeded(ref blurTextureHandle, blurTextureDescriptor);
        }
    
        private void UpdateBlurSettings()
        {
            if (material == null) return;
    
            // 获取 Volume 设置或使用默认值
            var volumeComponent =
                VolumeManager.instance.stack.GetComponent<CustomVolumeComponent>();
            float horizontalBlur = volumeComponent.horizontalBlur.overrideState ?
                volumeComponent.horizontalBlur.value : defaultSettings.horizontalBlur;
            float verticalBlur = volumeComponent.verticalBlur.overrideState ?
                volumeComponent.verticalBlur.value : defaultSettings.verticalBlur;
            material.SetFloat(horizontalBlurId, horizontalBlur);
            material.SetFloat(verticalBlurId, verticalBlur);
        }
    
        public override void Execute(ScriptableRenderContext context,
            ref RenderingData renderingData)
        {
            // 从池中获取 CommandBuffer
            CommandBuffer cmd = CommandBufferPool.Get();
    
            RTHandle cameraTargetHandle =
                renderingData.cameraData.renderer.cameraColorTargetHandle;
    
            UpdateBlurSettings();
    
            // 先使用第一个 Shader Pass 进行水平模糊
            Blit(cmd, cameraTargetHandle, blurTextureHandle, material, 0);
            // 再使用第二个 Shader Pass 进行垂直模糊
            Blit(cmd, blurTextureHandle, cameraTargetHandle, material, 1);
    
            // 执行命令缓冲区并释放回池
            context.ExecuteCommandBuffer(cmd);
            CommandBufferPool.Release(cmd);
        }
    
        public void Dispose()
        {
        #if UNITY_EDITOR
            if (EditorApplication.isPlaying)
            {
                Object.Destroy(material);
            }
            else
            {
                Object.DestroyImmediate(material);
            }
        #else
                    Object.Destroy(material);
        #endif
    
            if (blurTextureHandle != null) blurTextureHandle.Release();
        }
    }
    

    Volume 组件代码

    以下是 Volume 组件脚本的完整代码。

    using System;
    using UnityEngine.Rendering;
    
    [Serializable]
    public class CustomVolumeComponent : VolumeComponent
    {
        public BoolParameter isActive = new BoolParameter(true);
        public ClampedFloatParameter horizontalBlur =
            new ClampedFloatParameter(0.05f, 0, 0.5f);
        public ClampedFloatParameter verticalBlur =
            new ClampedFloatParameter(0.05f, 0, 0.5f);
    }
    

    模糊效果的自定义 Shader

    本节包含用于实现模糊效果的自定义 Shader 代码。

    Shader "CustomEffects/Blur"
    {
        HLSLINCLUDE
        
            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
            // Blit.hlsl 提供了顶点着色器 (Vert)、
            // 输入结构 (Attributes) 和输出结构 (Varyings)
            #include "Packages/com.unity.render-pipelines.core/Runtime/Utilities/Blit.hlsl"
    
            float _VerticalBlur;
            float _HorizontalBlur;
        
            float4 _BlitTexture_TexelSize;
        
            float4 BlurVertical (Varyings input) : SV_Target
            {
                const float BLUR_SAMPLES = 64;
                const float BLUR_SAMPLES_RANGE = BLUR_SAMPLES / 2;
                
                float3 color = 0;
                float blurPixels = _VerticalBlur * _ScreenParams.y;
                
                for(float i = -BLUR_SAMPLES_RANGE; i <= BLUR_SAMPLES_RANGE; i++)
                {
                    float2 sampleOffset =
                        float2 (0, (blurPixels / _BlitTexture_TexelSize.w) *
                            (i / BLUR_SAMPLES_RANGE));
                    color +=
                        SAMPLE_TEXTURE2D(_BlitTexture, sampler_LinearClamp,
                            input.texcoord + sampleOffset).rgb;
                }
                
                return float4(color.rgb / (BLUR_SAMPLES + 1), 1);
            }
    
            float4 BlurHorizontal (Varyings input) : SV_Target
            {
                const float BLUR_SAMPLES = 64;
                const float BLUR_SAMPLES_RANGE = BLUR_SAMPLES / 2;
                
                UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(input);
                float3 color = 0;
                float blurPixels = _HorizontalBlur * _ScreenParams.x;
                for(float i = -BLUR_SAMPLES_RANGE; i <= BLUR_SAMPLES_RANGE; i++)
                {
                    float2 sampleOffset =
                        float2 ((blurPixels / _BlitTexture_TexelSize.z) *
                            (i / BLUR_SAMPLES_RANGE), 0);
                    color +=
                        SAMPLE_TEXTURE2D(_BlitTexture, sampler_LinearClamp,
                            input.texcoord + sampleOffset).rgb;
                }
                return float4(color / (BLUR_SAMPLES + 1), 1);
            }
        
        ENDHLSL
        
        SubShader
        {
            Tags { "RenderType"="Opaque" "RenderPipeline" = "UniversalPipeline"}
            LOD 100
            ZWrite Off Cull Off
            Pass
            {
                Name "BlurPassVertical"
    
                HLSLPROGRAM
                
                #pragma vertex Vert
                #pragma fragment BlurVertical
                
                ENDHLSL
            }
            
            Pass
            {
                Name "BlurPassHorizontal"
    
                HLSLPROGRAM
                
                #pragma vertex Vert
                #pragma fragment BlurHorizontal
                
                ENDHLSL
            }
        }
    }
    
    In This Article
    Back to top
    Copyright © 2025 Unity Technologies — Trademarks and terms of use
    • Legal
    • Privacy Policy
    • Cookie Policy
    • Do Not Sell or Share My Personal Information
    • Your Privacy Choices (Cookie Settings)