【渲染流水线】[输出阶段]-[双缓冲机制]以UnityURP为例

  • 最终将处理结果写入颜色/深度/模板缓冲区
  • 可独立控制各通道写入权限‌

【从UnityURP开始探索游戏渲染】专栏-直达

双缓冲机制介绍

双缓冲(Double Buffer)是一种图形渲染中的常用技术,通过使用两个缓冲区(前台缓冲区和后台缓冲区)来解决图形渲染过程中的闪烁和撕裂问题。在Unity URP中,双缓冲机制主要用于管理渲染目标,确保渲染过程的平滑进行。

双缓冲的核心原理是:一个缓冲区用于当前显示的帧(前台缓冲区),另一个缓冲区用于绘制新帧(后台缓冲区)。当后台缓冲区完成绘制后,系统会执行交换操作,将两个缓冲区瞬时交换。现代GPU通常使用三重缓冲等多缓冲技术来进一步优化性能。

历史发展流程

双缓冲技术最早应用于图形显示领域,用于解决屏幕刷新和图形绘制之间的同步问题。在Unity引擎中,双缓冲机制随着渲染管线的演进不断优化:

  • 内置渲染管线时期‌:Unity早期版本使用内置渲染管线,双缓冲主要通过命令缓冲区(CommandBuffer)实现,开发者可以手动管理渲染目标的切换。
  • 可编程渲染管线(SRP)引入‌:Unity 2018版本引入可编程渲染管线概念,为双缓冲提供了更灵活的接口。
  • URP成熟期‌:在URP(Universal Render Pipeline)中,双缓冲机制被系统化封装,形成了SwapBufferSystem等高级抽象,优化了后处理流程。

内置管线与URP中的双缓冲变量

内置管线中的双缓冲变量

在内置渲染管线中,双缓冲主要通过以下变量和机制实现:

  • CommandBuffer:存储一系列渲染指令的容器,可用于设置渲染目标
  • RenderTexture:临时渲染纹理,常被用作后台缓冲区
  • BuiltinRenderTextureType:内置纹理类型,包括CurrentActiveCameraTarget

URP中的双缓冲变量

URP对双缓冲机制进行了更高级的封装,主要涉及以下变量和类:

  • SwapBufferSystem:URP中的双缓冲系统实现,包含m_Am_B两个SwapBuffer结构体
  • RenderTargetHandle:URP中表示渲染目标的句柄
  • ScriptableRenderPass:渲染通道基类,包含设置渲染目标的接口
  • UniversalRenderer:URP默认渲染器,管理双缓冲的初始化和交换

底层原理解析

底层实现核心类与变量

在Unity URP中,双缓冲机制主要通过SwapBufferSystem类实现,该类包含两个关键变量m_Am_B,分别代表前后缓冲区。具体实现位于UniversalRenderer类中,作为渲染目标管理系统的核心组件。

主要实现细节

  • SwapBuffer结构体:包含name(缓冲区名称)和rt(RenderTexture)两个字段
  • SwapBufferSystem类:管理两个SwapBuffer实例的交换逻辑
  • UniversalRenderer类:初始化并控制双缓冲系统的生命周期

缓冲区创建

  • UniversalRendererSetup方法中创建两个渲染目标,这些目标实际上是纹理支持的帧缓冲。

描述符控制

  • 通过cameraTargetDescriptor参数描述渲染目标的内存分配方式,可以在AddRenderPasses阶段修改这些参数来"劫持"渲染目标。

交换机制

  • URP使用SwapBufferSystem维护两个渲染目标缓冲区(m_Am_B),在渲染过程中交替使用,避免中间环节的重复拷贝。

同步控制

  • 双缓冲与VSync信号配合工作,确保缓冲区交换与屏幕刷新同步,防止画面撕裂.

双缓冲机制URP示例

后处理效果双缓冲实现

  • PostProcessFeature.cs

    后处理效果中如何使用临时渲染目标实现双缓冲交换,避免直接修改原始帧缓冲导致的问题

    csharp 复制代码
    using UnityEngine;
    using UnityEngine.Rendering;
    using UnityEngine.Rendering.Universal;
    
    public class CustomPostProcessPass : ScriptableRenderPass
    {
        private Material m_Material;
        private RenderTargetHandle m_TemporaryColorTexture;
        private RenderTargetHandle m_CameraColorTexture;
        
        public CustomPostProcessPass(Material material)
        {
            m_Material = material;
            m_TemporaryColorTexture.Init("_TemporaryColorTexture");
        }
    
        public override void Configure(CommandBuffer cmd, RenderTextureDescriptor cameraTextureDescriptor)
        {
            cmd.GetTemporaryRT(m_TemporaryColorTexture.id, cameraTextureDescriptor);
        }
    
        public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
        {
            CommandBuffer cmd = CommandBufferPool.Get("Custom Post Processing");
            
            // 双缓冲交换
            Blit(cmd, m_CameraColorTexture.Identifier(), 
                m_TemporaryColorTexture.Identifier(), m_Material);
            Blit(cmd, m_TemporaryColorTexture.Identifier(), 
                m_CameraColorTexture.Identifier());
                
            context.ExecuteCommandBuffer(cmd);
            CommandBufferPool.Release(cmd);
        }
    
        public override void FrameCleanup(CommandBuffer cmd)
        {
            cmd.ReleaseTemporaryRT(m_TemporaryColorTexture.id);
        }
    }

自定义渲染目标双缓冲系统

  • CustomBufferSystem.cs

    实现了一个简单的双缓冲系统示例,可用于需要多帧数据累积的效果如运动模糊

    csharp 复制代码
    using UnityEngine;
    using UnityEngine.Rendering;
    using UnityEngine.Rendering.Universal;
    
    public class CustomBufferSystem
    {
        private RenderTargetHandle[] m_Buffers = new RenderTargetHandle[2];
        private int m_CurrentBufferIndex = 0;
        
        public void Initialize(string name)
        {
            m_Buffers[0].Init(name + "_Buffer0");
            m_Buffers[1].Init(name + "_Buffer1");
        }
    
        public RenderTargetHandle GetCurrentBuffer()
        {
            return m_Buffers[m_CurrentBufferIndex];
        }
    
        public RenderTargetHandle GetNextBuffer()
        {
            return m_Buffers[(m_CurrentBufferIndex + 1) % 2];
        }
    
        public void Swap()
        {
            m_CurrentBufferIndex = (m_CurrentBufferIndex + 1) % 2;
        }
    
        public void Dispose(CommandBuffer cmd)
        {
            cmd.ReleaseTemporaryRT(m_Buffers[0].id);
            cmd.ReleaseTemporaryRT(m_Buffers[1].id);
        }
    }

URP多相机渲染双缓冲同步

  • MultiCameraRenderer.cs

    多相机渲染场景中如何使用双缓冲机制实现平滑的相机切换效果

    csharp 复制代码
    using UnityEngine;
    using UnityEngine.Rendering;
    using UnityEngine.Rendering.Universal;
    
    public class MultiCameraRenderer : MonoBehaviour
    {
        public Camera[] cameras;
        private RenderTexture[] buffers = new RenderTexture[2];
        private int currentBufferIndex = 0;
        
        void Start()
        {
            buffers[0] = new RenderTexture(Screen.width, Screen.height, 24);
            buffers[1] = new RenderTexture(Screen.width, Screen.height, 24);
        }
    
        void Update()
        {
            var renderer = (UniversalRenderPipelineAsset)GraphicsSettings.currentRenderPipeline;
            
            // 渲染到后台缓冲区
            int nextBufferIndex = (currentBufferIndex + 1) % 2;
            cameras[0].targetTexture = buffers[nextBufferIndex];
            
            // 交换缓冲区
            currentBufferIndex = nextBufferIndex;
            
            // 显示当前缓冲区
            Graphics.Blit(buffers[currentBufferIndex], null as RenderTexture);
        }
    }

【从UnityURP开始探索游戏渲染】专栏-直达

(欢迎点赞留言探讨,更多人加入进来能更加完善这个探索的过程,🙏)

相关推荐
淡海水1 小时前
【URP】[平面阴影]原理与实现
平面·unity·urp·阴影
weixin_424294679 小时前
Unity:游戏性能优化!之把分散在各个游戏角色GameObject上的脚本修改为在一个脚本中运行。这样做会让游戏运行更高效?
游戏·unity·性能优化
YF云飞19 小时前
车机两分屏运行Unity制作的效果
unity·游戏引擎·个人开发·车机
枯萎穿心攻击20 小时前
Unity VS UE 性能工具与内存管理
开发语言·游戏·unity·ue5·游戏引擎·虚幻·虚幻引擎
淡海水1 天前
【URP】Unity 插入自定义RenderPass
unity·游戏引擎·渲染·shader·renderpass
霜绛1 天前
Unity笔记(六)——Mathf、三角函数、坐标系、向量
笔记·学习·unity·游戏引擎
SmalBox1 天前
【渲染流水线】[逐片元阶段]-[混合Blend]以UnityURP为例
unity·渲染
DanmF--1 天前
Unity中的特殊文件夹
unity·c#·游戏引擎
野区捕龙为宠1 天前
Unity Netcode for GameObjects(多人联机小Demo)
java·unity·游戏引擎