高斯模糊性能优化
本文来自一次工作里碰到的问题,客户要求我们使用高斯模糊处理某些画面,但由于使用高斯模糊的区域大、多,帧率要求高,且从技术上看最好在窗口合成器中实现,从而导致了较为严重的性能问题,一方面 GPU 使用率飙升,另一方面系统帧率严重下降。因此我专门找到了一种于 2019 年在 SIGGRAPH 上提出的高斯模糊优化算法,代替我们原本的算法实现。从最终效果上来看,优化算法降低了 66% 的 GPU 性能消耗,CPU 消耗波动忽略,而高斯模糊的处理时间也降到了三分之一,效果极好,让原本几乎不能用的场景变得流畅。下面首先介绍传统高斯模糊算法。
常见高斯模糊方法
假设被模糊图片分辨率宽为
按照高斯模糊原始用法,渲染单个像素需要对周围
其中
但通过严格的数学证明,二维高斯模糊可以分为先横向后纵向,或先纵向后横向的两次一维模糊,两次模糊的核大小不变且相同,权值符合如下分布
因此渲染单个像素,仅需对其横向
但要注意的是,该方法需要一个中间缓冲(纹理)记录第一次一维模糊的结果,因此会额外增加
NonuniformBlur
由于高斯模糊的结果受核大小
高斯模糊的性能消耗大头在于对周围像素的采样频率,频率较大时会造成较高消耗。而 mipmap 各个层级的像素是原始纹理某个区域的像素平均值,因此文章作者使用 mipmap 这一数据结构来近似高斯模糊,并将采样频率降到较低水平。
同时,mipmap 在现代GPU上有较快的硬件生成速度(用计算着色器生成速度也很快),因此大尺度高斯模糊替换为本方法后,性能消耗会得到显著降低。
设
其中
或者为
从公式可以看到,采样频率不再与高斯核大小有关,只与图像分辨率有关,单个像素的采样频率为
同时,由于生成 mipmap,所以会额外增加三分之一的原始图片内存。
对一个 1920*720 的图片使用上述方法进行模糊,
#version 320 es
precision mediump float;
in vec2 texCoordVsOut;
uniform sampler2D srcTex;
out vec4 fragColor;
void main()
{
vec4 color = vec4(0.f, 0.f, 0.f, 0.f);
color += textureLod(srcTex, texCoordVsOut,11.f) * 0.000000;
color += textureLod(srcTex, texCoordVsOut,10.f) * 0.000000;
color += textureLod(srcTex, texCoordVsOut,9.f) * 0.000000;
color += textureLod(srcTex, texCoordVsOut,8.f) * 0.000000;
color += textureLod(srcTex, texCoordVsOut,7.f) * 0.000000;
color += textureLod(srcTex, texCoordVsOut,6.f) * 0.000000;
color += textureLod(srcTex, texCoordVsOut,5.f) * 0.089084;
color += textureLod(srcTex, texCoordVsOut,4.f) * 0.739691;
color += textureLod(srcTex, texCoordVsOut,3.f) * 0.156954;
color += textureLod(srcTex, texCoordVsOut,2.f) * 0.013316;
color += textureLod(srcTex, texCoordVsOut,1.f) * 0.000898;
color += textureLod(srcTex, texCoordVsOut,0.f) * 0.000057;
fragColor = vec4(color.rgb, 1.0f);
}
可以看到,不仅 render pass 从原来的两次降为1次,单个像素采样频率也只有12次。
NonuniformBlur再优化
从优化方案生成的着色器可以看到部分权重为0,因此还能剔除这些权重的采样来降低性能消耗(可以按小于0.0039进行剔除,因为0.0039对应8位子像素归一化后一个色阶的大小,即1/256,因此权重小于0.0039意味着该层对最终结果的影响不足一个色阶,可以忽略)。
故而优化后的着色器为
#version 320 es
precision mediump float;
in vec2 texCoordVsOut;
uniform sampler2D srcTex;
out vec4 fragColor;
void main()
{
vec4 color = vec4(0.f, 0.f, 0.f, 0.f);
color += textureLod(srcTex, texCoordVsOut,5.f) * 0.089084;
color += textureLod(srcTex, texCoordVsOut,4.f) * 0.739691;
color += textureLod(srcTex, texCoordVsOut,3.f) * 0.156954;
color += textureLod(srcTex, texCoordVsOut,2.f) * 0.013316;
fragColor = vec4(color.rgb, 1.0f);
}
可以看到,采样频率降低为4次。
参考文献
[1] Xu T, Ren X, Wu E. The Power of Box Filters: Real-time Approximation to Large Convolution Kernel by Box-filtered Image Pyramid[M]//SIGGRAPH Asia 2019 Technical Briefs. 2019: 1-4.
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek “源神”启动!「GitHub 热点速览」
· 我与微信审核的“相爱相杀”看个人小程序副业
· 上周热点回顾(2.17-2.23)
· 如何使用 Uni-app 实现视频聊天(源码,支持安卓、iOS)
· C# 集成 DeepSeek 模型实现 AI 私有化(本地部署与 API 调用教程)