常见高斯模糊方法
假设被模糊图片分辨率宽为 \(W\),高为 \(H\),假设高斯模糊核大小为 \(S\),半径为 \(R\)(不包含中心点,即 \(S=2×R+1\))。
按照高斯模糊原始用法,渲染单个像素需要对周围 \(S × S\) 个像素进行采样,并乘以对应的符合高斯分布的权值,公式为:
其中 \(x\)、\(y\) 为被采样像素到中心像素的横、纵距离,因此渲染一帧的时间复杂度为 \(O(W×H×S^2)\)。
但通过严格的数学证明,二维高斯模糊可以分为先横向后纵向,或先纵向后横向的两次一维模糊,两次模糊的核大小不变且相同,权值符合如下分布
因此渲染单个像素,仅需对其横向 \(S\) 个和纵向 \(S\) 个像素共 \(2S\) 个像素进行采样,因此渲染一帧的时间复杂度降为 \(O(W×H×S)\) 。
但要注意的是,该方法需要一个中间缓冲(纹理)记录第一次一维模糊的结果,因此会额外增加 \(O(W×H)\) 空间复杂度的消耗。
NonuniformBlur
由于高斯模糊的结果受核大小 \(S\) 与 \(\sigma\) 影响,二者越大图像越模糊,后者对性能没有影响,但前者的大小会显著影响程序性能,因此引入一种以高性能为目标的高斯模糊近似优化方法,该方法在《The Power of Box Filters: Real-time Approximation to Large Convolution Kernel by Box-filtered Image Pyramid 》[1] 提出,如下为太长不看版(本人总结):
高斯模糊的性能消耗大头在于对周围像素的采样频率,频率较大时会造成较高消耗。而 mipmap 各个层级的像素是原始纹理某个区域的像素平均值,因此文章作者使用 mipmap 这一数据结构来近似高斯模糊,并将采样频率降到较低水平。
同时,mipmap 在现代GPU上有较快的硬件生成速度(用计算着色器生成速度也很快),因此大尺度高斯模糊替换为本方法后,性能消耗会得到显著降低。
设 \(p_{Gaussian}\) 为一个像素高斯模糊结果,\(s(x,y)\) 为被采样像素值,\(l\) 为 mipmap 层数(注意,和 OpenGL ES 的层数是反过来的),\(w_{G}(l)\) 为在 mipmap 第 \(l\) 层的权值,\(S_{box}(l)\) 为在 mipmap 第 \(l\) 层采样的结果,公式为:
其中
或者为
从公式可以看到,采样频率不再与高斯核大小有关,只与图像分辨率有关,单个像素的采样频率为 \(⌊log_2max(W,H)⌋+1\),而每张图片模糊的时间复杂度为 \(O(W×H×(1+log_2max(W,H)))\)。
同时,由于生成 mipmap,所以会额外增加三分之一的原始图片内存。
对一个 1920*720 的图片使用上述方法进行模糊,\(\sigma\) 为 5,在运行时生成着色器如下所示:
#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.
标签:textureLod,高斯,srcTex,color,模糊,像素,texCoordVsOut,优化 From: https://www.cnblogs.com/tianrenbushuai/p/18498510