1143 字
6 分钟
RenderFeature 入门——基础概念

RenderFeature 入门知识#

本章将用最直白,最不绕弯子的话来讲解相关的概念,主要是帮助理解,并不是精确无歧义的概念。 Unity 版本为2022+,Unity6使用 RenderGraph 不做讲解,URP版本为14+。

什么是 RenderFeature#

RenderFeature(后面简称 RF) = 往 URP 的“渲染流水线”里插入一段你自己的代码。

什么是 Pass#

Pass 可以理解成“一帧渲染过程中,一个具有明确目的的步骤/阶段”;真正执行渲染操作。

模板流程函数#

一个 RenderFeature 的基本流程#

Create()
↓
SetupRenderPasses()
↓
AddRenderPasses()
↓
Pass 的 Configure / OnCameraSetup
↓
Pass 的 Execute
↓
Pass 的 OnCameraCleanup
↓
Dispose()
TIP

其中的 SetupRenderPasses() 函数需要特殊注意一下,他在很多老教程中不会出现,但在现代的 RF 中,是一个非常重要的环节。

Create() —— 创建 Pass#

public override void Create()
{
pass = new MyRenderPass();
}

Unity 创建这个 RenderFeature 的时候,会调用Create()函数。

在这个函数中,我们通常会做如下的一下操作:

  • 创建 Pass
  • 创建 Material
  • 初始化参数
  • 设置 RenderPassEvent
NOTE

Create() 不是每一帧执行一次的地方;它主要负责初始化。

AddRenderPasses() —— 把 Pass 真正加入流水线#

这是核心函数之一。

public override void AddRenderPasses(ScriptableRenderer renderer,ref RenderingData renderingData)
{
renderer.EnqueuePass(pass);
}

函数中 EnqueuePass 字段是核心,可以将其理解成:下次渲染的时候,执行这个 Pass。它实际做的是将 Pass 放入渲染队列,让其在渲染队列中排队。

SetupRenderPasses() —— Pass 加入前的设置#

TIP

现代 URP 引入的函数,为了解决 Camera Target 并不是在 RF 初始化阶段就一定会准备好的问题。 因此将流程拆开,让函数职责更加单一具体。

OnCameraSetup() —— 资源准备#

在真正 Excute 执行之前,把这个 Pass 需要的渲染资源准备好。

Excute() —— 真正的执行者#

Excute 是每帧真正执行渲染代码的地方。

OnCameraCleanup() —— 清理资源#

清理本次摄像机渲染需要的资源。现代 RTHandle 的生命周期管理方式和以前的不完全一样,所以并不会在函数中机械的 Release() 所有东西。

Dispose() —— RenderFeature 被销毁时清理#

这个部分是整个 RF 不用了,把他彻底清理掉。

Render Target#

什么是 Render Target#

可以把 Renertarget 理解成:CPU 用来存放当前渲染结果的一块“画布”。

GPU 画出来的颜色,深度等数据,最终就会存放在某个 RenderTarget 里。

屏幕就是一个 RenderTarget#

当渲染正常场景时,本质上就是在往某个目标里写数据。

RTHandle#

什么是 RTHandle#

这个 RTHandle 并不是 “一张纹理本身”,更合适的理解是:RTHanle 是 Unity 用来管理RenderTarget 的一个“句柄/引用”。

// 我准备保存一张“临时 GPU 画布”的引用。
RTHandle temppraryRT;

为什么 URP 改为使用 RTHandle#

因为 URP 需要处理很多不同情况:

  • 不同分辨率
  • Camera 改分辨率
  • 动态分辨率
  • XR
  • 不同尺寸的 RenderTarget

RTHandle可以帮助管理这些 RenderTarget。

Blit#

什么是 Blit#

Blit = 把一张画布上的内容,复制/绘制到另一张画布上。

Blitter.BlitCameraTexture(
cmd,
cameraColorTarget,
temporaryRT
);
// 把 Camera 的画面 Blit 到 temporaryRT

Blit 通常不是简单的复制操作,而是把源纹理作为输入,在目标 RenderTarget 上进行一次绘制,可以理解为临摹一遍。

Blit 的最佳实践:Blit + Material#

Blitter.BlitCameraTexture(
cmd,
temporaryRT,
cameraColorTarget,
material,
0
);
// 读取 TemporaryRT,用 Material 对其进行处理,最后将结果写回 Camera
// 实际参与的是 Shader 而不是 Material

而 Blit 就负责把画面“送过去”和“写回来”。

Blitter.BlitCameraTexture(
cmd,
source,
destination,
material,
0
);
// Source 的内容,经过 Material 的 Shader,输出到 destination。

RF 中操作 API#

RenderPassEvent —— 什么时候执行#

插入的这个 Pass 在渲染的哪个阶段插入,例如:RenderPassEvent.AfterRenderingOpaques,是在不透明物体渲染完以后执行。

简化的渲染流程阶段:

BeforeRendering
↓
BeforeRenderingOpaques
↓
AfterRenderingOpaques
↓
BeforeRenderingTransparents
↓
AfterRenderingTransparents
↓
AfterRendering

CommandBuffer 命令#

cmd.SetGlobalXXX#

可以将其理解为:给 Shader 设置一个全局变量,不是针对某一个特定 Material 设置 ,而是设置到全局 Shader 环境。

cmd.SetRenderTarget#

接下来将要画东西,画到这个 RenderTarget 上。

cmd.ClearRenderTarget#

他的意思是,把当前 Render Target 清空。

cmd.ClearRenderTarget(
true, // 清空深度
true, // 清空颜色
Color.black
);

RenderingUtils.ReAllocateIfNeeded#

常见错误#

在现代 RF 中,在 AddRenderPasses 里面获取 Camera Target 是不安全的,在现代 URP 下这可能导致警告甚至是运行问题。

正确的思路应该是在 SetupRenderPasses 中获取,这点非常重要。