关于webgl:webgl智慧楼宇发光系列之线性采样下高斯模糊

33次阅读

共计 2467 个字符,预计需要花费 7 分钟才能阅读完成。

[toc]

webgl 智慧楼宇发光系列之线性采样下高斯含糊

后面一篇文章 <webgl 智慧楼宇发光成果算法系列之高斯含糊 >,   咱们晓得了 高斯含糊的实质原理,就是对每个像素,依照正态分布的权重去获取周边像素的值进行均匀,是一种卷积操作。

同时咱们能够指定周边像素的数量,比方能够是 3X3,或者 5X5,通用的表白就是 N X N,数字 N 通常称之为含糊半径,这在之前的文章的代码中有体现(uRadius):

uniform float uRadius;
float gaussianPdf(in float x, in float sigma) {return 0.39894 * exp( -0.5 * x * x/( sigma * sigma))/sigma;
}
void main() {for( int i = 1; i < MAX_KERNEL_RADIUS; i ++) {float x = float(i);
    if(x > radius){break;}
    ...
  }
  vec4 result = vec4(1.0) - exp(-diffuseSum/weightSum * uExposure);
  gl_FragColor = result;
}
`

效率问题

通常,咱们心愿含糊的成果越强烈,含糊半径就会要求越大。所谓的半径就是下面的数字 N。
咱们晓得,要实现一个 NxN 大小的高斯含糊,在纹理的每个像素点,都须要去获取周边 N 个像素点。因为 1024_1024 大小的纹理,要实现 33 33 大小的高斯含糊,须要拜访大略 1024 1024 _ 33 * 33≈11.4 亿个纹理像素,能力利用整个图像的含糊成果。

为了取得更无效的算法,咱们来看看高斯函数的一些个性:

  • 二维高斯函数能够通过将两个一维高斯函数相加来计算。
  • 散布为 2σ 的高斯函数等于散布为 σ 的两个高斯函数的乘积。

高斯函数的这两个属性为咱们提供了进行大量优化的空间。

基于第一个属性,咱们能够将二维高斯函数分成两个一维函数。在应用片段着色器的状况下,咱们能够将高斯滤镜分为程度含糊滤镜和垂直含糊滤镜,在渲染后仍可取得精确的后果。这个时候,1024_1024 大小的纹理,要实现 33 33 大小的高斯含糊,须要拜访大略 1024 1024 _ 33*2≈6,900 万个纹理提取。这种优化明细缩小了一个量级。文章《webgl 智慧楼宇发光成果算法系列之高斯含糊》曾经实现了这一优化。

第二个属性可用于绕过平台上的硬件限度,这些平台仅在一次 pass 中仅反对无限数量的纹理提取。

线性采样

到此,咱们晓得了把一个二维的高斯含糊 拆散成两个一维的高斯含糊。效率上也有了大幅度的进步。然而实际上,咱们还能够通过线性采样的个性进一步提高效率。

咱们晓得,要获取一个像素信息,就要做一次贴图的读取。这就象征 33 个像素信息,就须要做 33 次贴图的读取操作。然而因为在 GPU 下面能够随便进行双线线性插值,而没有额定的性能耗费。这就意味着,如果咱们不再像素的中心点读取贴图,就能够取得多个像素的信息。如下图所示:

假如两个像素,咱们在像素 1 中心点读取贴图就是获取像素 1 的色彩,在像素 2 中心点读取贴图就是获取像素 2 的色彩;而在像素 1 中心点和像素 2 中心点的某个地位读取贴图,则会获取像素 1 和像素 2 的色彩的加权均匀的成果。

因为咱们做高斯含糊的时候,自身就是获取周边相邻元素的加权平均值,因而利用线性采样的这个个性,能够把本来 2 个像素的采样,缩小为一次采样。如果本来 33 次采样,则能够缩小到 17 次。

对于两个纹素的采样,须要调整坐标使其与纹素 #1 核心的间隔等于纹素#2 的权重除以两个权重之和。同样的,坐标与纹素#2 核心的间隔应该等于纹素#1 的权重除以两个权重之和。

而后咱们就有了计算线性采样高斯滤波的权重和位移公式:

代码解说

  • 首先定义一个 uniform 变量,该变量示意是否启用线性采样的办法:
uniform bool uUseLinear;
  • 而后如果应用线性采样,就把本来的采样次数缩小一半:
 if(uUseLinear){radius = uRadius / 2.0;}
  • 再而后,如果应用线性采样,就应用上述的公式进行像素提取:
if(uUseLinear){
      // http://rastergrid.com/blog/2010/09/efficient-gaussian-blur-with-linear-sampling/
      float t1 = 2.0 * x - 1.0,t2 = 2.0 * x ;
      float w1 = gaussianPdf(t1,fSigma);
      float w2 = gaussianPdf(t2,fSigma);
      w = w1 + w2;
      t = (t1 * w1 + t2 * w2) / w;
    }

    vec2 uvOffset = uDirection * invSize * t;
    vec4 sample1 = texture2D(uColorTexture, vUv + uvOffset).rgba;
    vec4 sample2 = texture2D(uColorTexture, vUv - uvOffset).rgba;
    diffuseSum += (sample1 + sample2) * w;
    weightSum += 2.0 * w;

最终的绘制成果如下:

其中右边的未应用线性采样的机制,而左边的应用了线性采样,能够看出左边再缩小了一半的采样的状况下,成果和右边的根本没有差异。

而效率上,通过测试,左边比右边大略进步了 40% 的渲染效率。

总结

通过线性采样的机制,咱们能够看到效率进步了近一倍。这在一些对性能要求高得场景或者挪动终端是很有意义。

其实要做出一个好的发光成果,波及到相干算法是很多了,而且细节之处都须要关注。

先看看咱们曾经做了得一些发光楼宇得案例吧, 以下都是再简略模型(立方体)+ 贴图 + 光照 + 发光 进去得成果,如果模型层面在优化,应该还能够有更酷成果:

如果对可视化感兴趣,能够和我交换,微信 541002349. 另外关注公众号“ITMan 彪叔”能够及时收到更多有价值的文章。

参考文档

参考文档:http://rastergrid.com/blog/2010/09/efficient-gaussian-blur-with-linear-sampling/
本文局部素材应用了参考文档中的内容。

正文完
 0