[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/
本文局部素材应用了参考文档中的内容。