Unity Shader 系列(三十一):多通道渲染与 RenderTexture Ping-Pong 技术
为什么需要多通道渲染?
单帧渲染可以绘制出精美的画面,但许多高级效果本质上需要**跨帧的”记忆”**——流体模拟需要上一帧的速度场,屏幕空间轨迹需要保存历史像素,TAA 需要累积历史帧。这些效果的核心技术在 Unity 中称为 RenderTexture Ping-Pong:将渲染结果写入纹理而非直接输出到屏幕,下一帧再读取这张纹理,实现跨帧数据传递。
本篇将从 Unity 的视角彻底讲清这套技术:RenderTexture 创建与管理、三种 Blit 方案的选择、完整 Ping-Pong 实现,以及两个可直接在项目中使用的示例。
RenderTexture 基础
创建与配置
在 Unity 中创建 RenderTexture 有两种方式:Inspector 配置和 C# 代码创建。
Inspector 配置(适合静态分辨率、不频繁更改的 RT):
- Project 窗口右键 → Create → Render Texture
- 设置 Size、Color Format(推荐
RGBAFloat用于物理模拟,RGBA32用于普通后处理) - 设置 Filter Mode 和 Wrap Mode
C# 代码创建(适合运行时动态分辨率):
1 | |
RenderTextureFormat 选择指南
| 格式 | 位深 | 用途 |
|---|---|---|
ARGB32 |
8bpc | 普通颜色,后处理 |
ARGBHalf |
16bpc | HDR 颜色,Bloom |
ARGBFloat |
32bpc | 物理模拟,精度要求高 |
RFloat |
32bpc 单通道 | 高度图,深度数据 |
RGFloat |
32bpc 双通道 | 2D 速度场 |
三种 Blit 方案对比
Unity 中有三种将处理结果写入 RenderTexture 的方式,适用场景不同:
方案一:Graphics.Blit()
最简单,适合简单的全屏后处理。
1 | |
缺点:在 URP 管线中与渲染顺序不兼容,可能产生时序问题。
方案二:CommandBuffer.Blit()
适合需要精确控制执行时机的场景,可插入渲染管线特定阶段。
1 | |
方案三:URP Renderer Feature(推荐方案)
这是 URP 中的标准做法,通过自定义 ScriptableRendererFeature 注入渲染 Pass,与管线生命周期完全集成,支持 RTHandle 系统。
1 | |
实战示例一:Game of Life(细胞自动机)
这是 Ping-Pong 最经典的演示:每帧根据周围邻居数量判断细胞生死。
C# 控制脚本
1 | |
Game of Life URP Shader
1 | |
实战示例二:屏幕空间轨迹拖尾效果
将当前帧叠加到上一帧,并每帧淡出,产生运动轨迹。
1 | |
对应的 C# 管理脚本:
1 | |
RTHandle 系统详解
URP 2021+ 推荐使用 RTHandle 代替裸 RenderTexture,主要优势:
- 自动分辨率缩放:配合 URP 的动态分辨率(DRS)自动调整大小
- 统一的生命周期管理:通过
RTHandles分配器统一管理,避免内存泄漏 - 与
BlitterAPI 集成:Blitter.BlitCameraTexture正确处理平台差异(Metal Y 轴翻转等)
1 | |
游戏实际应用场景
| 效果 | Ping-Pong 数据 | 用途 |
|---|---|---|
| 流体/烟雾模拟 | 速度场 + 密度场 | 环境特效 |
| 布料模拟 | 顶点位置 + 速度 | 角色服装 |
| 屏幕空间轨迹 | 累积颜色帧 | 子弹时间、粒子拖尾 |
| TAA 抗锯齿 | 历史颜色帧 | 画质提升 |
| 动态阴影图 | 阴影累积贴图 | 软阴影优化 |
| 反应扩散 | 化学浓度 AB | 有机纹理生成 |
性能考量
- 格式选择:物理模拟用
ARGBFloat,纯显示用ARGBAHalf,节省 50% 显存带宽 - 分辨率降采样:模拟缓冲可以用屏幕分辨率的 1/2 或 1/4,显示时双线性放大,效果差异极小
- Mobile 注意:
ARGBFloat在部分 Mali GPU 上不支持作为 RT,需要降级为ARGBHalf - **避免每帧
new RenderTexture**:使用RenderingUtils.ReAllocateIfNeeded或缓存 RT 对象 - RTHandle vs 裸 RenderTexture:RTHandle 在 URP 中是标准,裸 RenderTexture 在管线内部使用可能产生时序问题
ShaderGraph 实现思路
ShaderGraph 本身不支持 Ping-Pong(需要 C# 脚本管理 RT),但可以用于构建显示 Shader:
- 在 C# 中管理 Ping-Pong RT,将结果通过
Material.SetTexture传入 - ShaderGraph 中添加
Texture2D属性节点接收模拟结果 - 连接到
Sample Texture 2D→ 颜色/发光输出
模拟逻辑本身(需要读取上一帧)必须用 HLSL Shader 手写,ShaderGraph 无法表达自反馈逻辑。
多通道 Ping-Pong 是 Unity 中实现持久化 GPU 状态的核心工具,掌握它意味着你可以在 GPU 上运行完整的物理世界并无缝集成到 URP 渲染管线中。