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
NOTECreate() 不是每一帧执行一次的地方;它主要负责初始化。
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 到 temporaryRTBlit 通常不是简单的复制操作,而是把源纹理作为输入,在目标 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 ↓AfterRenderingCommandBuffer 命令
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 中获取,这点非常重要。