Unity SRP渲染管线的设计解析

目的为对 SRP 的框架进行理解,理解其 必要的部分与原理,并非对非必要细节进行逐词翻译。


一、SRP 渲染管线与 Unity 底层调用渲染指令的联系


二、RenderPipelineAsset

1.1 成员

1.1.1 虚拟成员

属性 (get; set;)
  • defaultMaterial (在编辑器中物体的默认 material)
  • defaultShader (在编辑器中物体的默认 shader)
  • renderPipelineShaderTag (对应于 shaderlab 的 renderpipline tag)
方法
  • OnValidate() (在编辑器中修改参数时调用)

1.1.2 抽象成员

方法
  • CreatePipeline(this) (实例化 RenderPipeline)

三、RenderPipeline

关键方法

  • RenderInternal(context, cameras) (调用 Render)
  • Render(context, cameras) (abstract)

以上为 SRP 的最基础的架构,即使没有 URP 对于 Renderer、Pass、RendererFeature 的抽象,依然可以通过直接对 context 进行指令填充,实现简单的渲染管线。

渲染基础流程:

  • RenderPipeline.Render(context, cameras[])
    • [逐相机循环] ↓
      • context.SetupCameraProperties(camera) ← 同步 MVP 矩阵到 GPU ↓
      • camera.TryGetCullingParameters() ← 准备剔除参数 ↓
      • context.Cull() ← CPU 侧视锥体/遮挡剔除 ↓
      • cmd.ClearRenderTarget() ← 清理 FrameBuffer ↓
      • context.DrawRenderers() [Opaque] ← 不透明物体渲染 ↓
      • context.DrawSkybox() ← 天空盒渲染 ↓
      • context.DrawRenderers() [Transparent] ← 半透明物体渲染 ↓
      • context.Submit() ← 将 CommandBuffer 批量提交 GPU 执行

URP 对 SRP 的拓展

目的为 URP 基于 SRP 的框架的拓展进行理解,理解其 必要的部分与原理,并非对非必要细节进行逐词翻译。


一、UniversalRenderPipelineAsset

1.1 关键改动或新增

1.1.1 成员

字段
  • m_RendererDataList
  • m_Renderers
方法
  • CreatePipeline(this)
    • 核心逻辑:
      1. rendererdata.create() added

二、UniversalRenderPipeline

1.1 关键改动或新增

1.1.1 成员

属性
  • asset (pipelineAsset)
方法
  • Render(...)
    • 核心逻辑执行流:
      1. sortCameras (深度 + 是否渲染到 RenderTexture)
      2. RenderCameraStack(Base)
      3. for x in Base.cameraStack:
        • RenderSingleCamera(x)
  • RenderSingleCamera(...)
    • 核心逻辑执行流:
      1. TryGetCullingParameters
      2. context.Cull()
      3. 读取 camera.UniversalAdditionalCameraData
      4. 获取绑定 Renderer (从 asset 获得)
      5. 装配 renderingData
      6. renderer.AddRenderPasses (内部触发 foreach features.AddRenderPasses)
      7. renderer.Setup(context, ref renderingData)
      8. renderer.Execute(context, ref renderingData)

三、ScriptableRendererData

1.1 成员

1.1.1 正常成员

属性
  • rendererFeatures (SO)

1.1.2 抽象成员

方法
  • Create(this) (实例化 Renderer)

四、ScriptableRenderer

1.1 成员

1.1.1 正常成员

字段
  • asset
构造方法
  • Renderer(...)
    • 核心逻辑执行流:
      1. new xxxpass(...) 等…
      2. this.asset = asset
方法
  • Execute(...)
    • 核心逻辑执行流:
      1. for pass in passes:
        • pass.OnCameraSetup(...)
      2. context.ExecuteCommandBuffer(cmd)
      3. SortStable(passes) by renderpassEvent
      4. SetBlockRanges (设置每个 Block 包括的 passIndexRange, 一般共四个)
      5. for pass in passes:
        • pass.Configure(...)
      6. ExecuteBlock(...) 循环四次 (BeforeRenderingMainRenderingOpaqueMainRenderingTransparentAfterRendering):
        • for pass in passesInBlock:
          • setRendererPassAttachments(...) (判断并懒设置 RenderTarget)
          • pass.Execute(...)
      7. for pass in passes:
        • pass.OnCameraCleanup(...)

1.1.2 抽象成员

方法
  • Setup(...)
    • 核心逻辑执行流:
      1. alloc cameraTarget from cameraData
      2. alloc RT and passes form passes in features configureinput
      3. Enqueue(xxxpass)
      4. features.SetupRenderPasses(...)

五、ScriptableRenderPass

1.1 成员

1.1.1 正常成员

属性
  • colorAttachments
  • depthAttachment
  • clearFlag
  • clearColor
  • renderPassEvent
  • m_Input
方法
  • ConfigureTarget()
  • ConfigureClear()
  • ConfigureInput()

1.1.2 抽象成员

方法
  • Execute(...)

1.1.3 虚拟成员

方法
  • OnCameraSetup(...)
  • Configure(...)
  • OnCameraCleanup(...)
  • ResetRenderTarget(...)

六、ScriptableRendererFeature

1.1 成员

1.1.1 抽象成员

方法
  • Create(...)
  • AddRenderPasses(...)

1.1.2 虚拟成员

方法
  • SetupRenderPasses(...)
  • Dispose(...)

以上对于 URP 的 必要的部分与原理 进行了理解与分析,以下分别为实例化渲染管线与每帧渲染的精简示意图:

graph TD
    A[Project Settings] -->|在 Graphics 槽位中分配| B(UniversalRenderPipelineAsset)
    
    B -.->|Unity 底层事件触发| C[RenderPipelineManager]
    C -->|1. 请求创建管线实例| D(Asset.CreatePipeline)
    
    D -->|2. 提取并调用| E[Asset.CreateRenderers]
    E -->|3. 根据传入的 RendererData| F(实例化 UniversalRenderer)
    
    F -.->|4. Renderer 构造时, 提取 Data 里的 Features| G[遍历 ScriptableRendererFeature]
    G -.->|5. 触发 Feature 生命周期的第一步| H(feature.Create)
    
    H -.->|6. 开发者手写逻辑| I[new MyCustomPass: 实例化 Pass]

以上为管线实例化过程

graph TD
    A[UniversalRenderPipeline.Render 传入多个摄像机] --> B(1. SortCameras: 按 TargetTexture 有无 以及 Depth 大小排序)
    B --> C[2. 主循环:遍历所有 Camera]
    
    C --> D{检查 Camera 的 RenderType}
    
    D -- Overlay --> E[直接忽略跳过<br>不能作为独立主入口]
    
    D -- Base --> F[3. 进入 RenderCameraStack]
    
    F -.-> G[3.1 执行 RenderSingleCamera<br>渲染 Base 摄像机本身]
    G -.-> H[3.2 遍历 Base 相机的 cameraStack]
    H -.-> I[3.3 依次执行 RenderSingleCamera<br>渲染叠加的 Overlay 摄像机]

以上为在 RenderSingleCamera 之前的相机排序过程

graph TD
    A[RenderSingleCamera] --> B(① renderer.AddRenderPasses)
    A --> C(② renderer.Setup)
    A --> D(③ renderer.Execute)
    
    B -.-> B1[Feature.AddRenderPasses]
    B1 -.-> B2[① 声明需求 ConfigureInput<br>② 占位入队 EnqueuePass<br>状态: 拿着假Target]
    
    C -.-> C1[遍历队列查需求 pass.input]
    C1 -.-> C2[根据需求分配真实 RenderTarget]
    C2 -.-> C3[官方基础 Pass 入队]
    C3 -.-> C4[Feature.SetupRenderPasses]
    C4 -.-> C5[拿到真实Target, 替换Pass中的假Target]
    
    D -.-> D1[SortStable: 按 Event 全局排序]
    D1 -.-> D2[Block 归纳: 划定 startIndex 与 endIndex]
    D2 -.-> D3[调用所有 Pass 的 Configure]
    D3 -.-> D4[按 Block 顺序执行]
    D4 -.-> D5[Block 内循环: 懒设置 SetRenderTarget]
    D5 -.-> D6[调用 pass.Execute]

以上为在 RenderSingleCamera 之后的 URP 的渲染过程