前言
半透明渲染一直是实时渲染管线中最头疼的性能瓶颈之一。半透明物体无法写入深度(Z-Write Off),既享受不到硬件 Early-Z 剔除的红利,又会带来大量 Overdraw(过度绘制)和复杂的排序开销。
本文从五个维度系统梳理一套半透明渲染的优化思路:减少 Overdraw、降分辨率渲染、Alpha Blend 与 Alpha Test 的硬件取舍、渲染队列与合批、以及算法层面的替代方案。
一、减少 Overdraw(过度绘制)
Overdraw 是半透明渲染最大的性能杀手。一个像素若被多层半透明物体覆盖,就会执行多次 Fragment Shader,导致极高的 Fillrate(填充率)压力。
1.1 Mesh Tightening(紧致化网格)
对于 UI、2D 贴图或大片面片粒子,不要使用简单的 Quad。通过增加少量顶点,把全透明(Alpha = 0)区域从 Mesh 边界裁剪掉,让网格紧贴实际有颜色的区域。
用一点顶点处理开销,换取大量像素填充率开销,是非常划算的交易。
Unity 中可以使用 Sprite Editor 的 Tight Mesh 模式,或者通过 SpritePacker / TextMeshPro 自动生成紧致网格;对于粒子系统,TextureSheet 配合自定义网格也能显著降低 Fillrate。
1.2 控制粒子层级与重叠
制作浓烟、爆炸等特效时:
- 严格限制发射器的生成数量与粒子生命周期;
- 优先使用少量高质量、带法线和光照的 Sprite,而不是堆叠成百上千个简单的半透明面片;
- 利用 Soft Particle、Distortion 等手段提升单片质量,减少层数。
二、降分辨率渲染与上采样
这是现代引擎处理体积云、体积雾、大规模粒子系统时的标配技术。
2.1 半分辨率 / 四分之一分辨率渲染
在独立的 Pass 中,将半透明对象(或体积效果)渲染到尺寸为屏幕 1/2 或 1/4 的 Render Target 上:
1 | |
2.2 双边滤波上采样(Bilateral Upsampling)
直接 Bilinear 放大会在物体边缘产生光晕和锯齿。正确的做法是结合全分辨率深度图做边缘保留滤波:当邻居像素的深度差异过大时降低其权重,从而在交界处保持锐利。
1 | |
三、Alpha Blend vs Alpha Test 的硬件权衡
处理树叶、草丛、铁丝网等边缘半透明时,常需要在 Alpha Blend 和 Alpha Test(clip / discard)之间做选择。结论与硬件架构强相关:
3.1 移动端(TBDR 架构)
移动端 GPU 高度依赖 HSR(Hidden Surface Removal)。在 Fragment Shader 中使用 clip() / discard 会破坏硬件 Early-Z,甚至导致整条流水线停滞。
对密集植被,有时牺牲一点排序正确性,改用 Alpha Blend,反而比 Alpha Test 更快。
3.2 PC / 主机端(IMR 架构)
IMR 对 clip() 的惩罚没有移动端那么致命。配合 Z-Prepass(先用 Alpha Test 写入深度,再做光照计算)能有效剔除被遮挡像素:
1 | |
四、渲染队列与合批优化
半透明物体的排序和状态切换会给 CPU 带来显著压力。
4.1 减少材质与 Blend State 切换
将表现相近的半透明物体合并到同一材质和渲染队列。在 URP 中要充分利用 SRP Batcher,前提是 Shader 的 CBUFFER 结构设计规范、所有材质属性都放在 UnityPerMaterial 中。
4.2 GPU 驱动的排序
传统 CPU 从后向前排序,在面对海量半透明实例(GPU 粒子、草海)时性能极差。可在 Compute Shader 中:
- 计算每个实例到相机的距离;
- 使用 Bitonic Sort(双调排序)或 Radix Sort(基数排序)完成 GPU 排序;
- 通过 DrawMeshInstancedIndirect 发起一次性绘制。
这样既消除了 CPU 排序瓶颈,也避免了 SetPassCall 爆炸。
五、算法层面的替代方案
5.1 抖动半透明(Stochastic / Dithered Transparency)
使用 Bayer Matrix 之类的噪波贴图,根据 Alpha 在屏幕空间随机 clip 掉一部分像素。本质上是把半透明转换为不透明(Alpha Test),再配合 TAA 在多帧间平滑噪点。
1 | |
适用场景:
- 头发、密集草丛;
- LOD 切换时的渐变(Cross-fade);
- 彻底回避半透明排序问题。
5.2 加权顺序无关半透明(WBOIT)与 A-Buffer
WBOIT(Weighted Blended OIT)在像素级别避免了排序,但视觉精度有限;更高精度的 A-Buffer / Per-Pixel Linked List 可在 Compute Shader 中维护每像素的片元链表。在显存压力大时:
限制最大记录层数(如只保留前 4 层),直接丢弃贡献度极低的深层像素。
这是性能与质量之间一个非常实用的折中。
总结
| 优化方向 | 核心收益 | 适用场景 |
|---|---|---|
| Mesh Tightening | 降低 Fillrate | UI、Sprite、粒子面片 |
| 降分辨率 + Bilateral Upsample | 大幅降低 Shader 开销 | 体积云、体积雾 |
| Alpha Blend / Test 取舍 | 适配硬件架构 | 植被、铁丝网 |
| SRP Batcher + GPU Sort | 降低 CPU / DrawCall 压力 | 海量粒子、草海 |
| Dithered Transparency / OIT | 消除排序问题 | 头发、LOD 渐变、复杂叠加 |
实际项目中,往往需要根据视觉需求 + 目标硬件 + 内容类型组合多种技术。下次遇到半透明性能瓶颈时,可以先定位是 Fillrate 瓶颈、排序瓶颈还是 DrawCall 瓶颈,再对症下药。