前面一篇文章 <webgl智慧樓宇發(fā)光效果算法系列之高斯模糊>, 我們知道了 高斯模糊的本質(zhì)原理贝咙,就是對每個像素此叠,按照正態(tài)分布的權(quán)重去獲取周邊像素的值進行平均,是一種卷積操作。
同時我們可以指定周邊像素的數(shù)量悔雹,比如可以是3X3,或者5X5欣喧,通用的表達就是N X N腌零, 數(shù)字N通常稱之為模糊半徑,這在之前的文章的代碼中有體現(xià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;
}
`
效率問題
通常唆阿,我們希望模糊的效果越強烈益涧,模糊半徑就會要求越大。所謂的半徑就是上面的數(shù)字N驯鳖。
我們知道闲询,要實現(xiàn)一個NxN大小的高斯模糊久免,在紋理的每個像素點,都需要去獲取周邊N個像素點扭弧。因為1024*1024大小的紋理阎姥,要實現(xiàn)33 * 33 大小的高斯模糊,需要訪問大概1024 * 1024 * 33 * 33≈11.4億個紋理像素鸽捻,才能應用整個圖像的模糊效果呼巴。
為了獲得更有效的算法,我們來看看高斯函數(shù)的一些特性:
- 二維高斯函數(shù)可以通過將兩個一維高斯函數(shù)相加來計算泊愧。
- 分布為2σ的高斯函數(shù)等于分布為σ的兩個高斯函數(shù)的乘積伊磺。
高斯函數(shù)的這兩個屬性為我們提供了進行大量優(yōu)化的空間。
基于第一個屬性删咱,我們可以將二維高斯函數(shù)分成兩個一維函數(shù)屑埋。在使用片段著色器的情況下,我們可以將高斯濾鏡分為水平模糊濾鏡和垂直模糊濾鏡痰滋,在渲染后仍可獲得準確的結(jié)果摘能。 這個時候,10241024大小的紋理敲街,要實現(xiàn)33 * 33 大小的高斯模糊团搞,需要訪問大概1024 * 1024 * 332≈6,900萬個紋理提取。這種優(yōu)化明細減少了一個量級多艇。文章 《webgl智慧樓宇發(fā)光效果算法系列之高斯模糊》已經(jīng)實現(xiàn)了這一優(yōu)化逻恐。
第二個屬性可用于繞過平臺上的硬件限制,這些平臺僅在一次pass中僅支持有限數(shù)量的紋理提取峻黍。
線性采樣
到此复隆,我們知道了把一個二維的高斯模糊 分離成兩個一維的高斯模糊。效率上也有了大幅度的提高姆涩。但是實際上挽拂,我們還可以通過線性采樣的特性進一步提高效率。
我們知道骨饿,要獲取一個像素信息亏栈,就要做一次貼圖的讀取。這就意味33個像素信息宏赘,就需要做33次貼圖的讀取操作绒北。 但是由于在GPU上面可以隨意進行雙線線性插值,而沒有額外的性能消耗置鼻。 這就意味著镇饮,如果我們不再像素的中心點讀取貼圖,就可以獲得多個像素的信息。 如下圖所示:
假設兩個像素储藐,我們在像素1中心點讀取貼圖就是獲取像素1的顏色俱济,在像素2中心點讀取貼圖就是獲取像素2的顏色;而在像素1中心點和像素2中心點的某個位置讀取貼圖钙勃,則會獲取像素1和像素2的顏色的加權(quán)平均的效果蛛碌。
因為我們做高斯模糊的時候,本身就是獲取周邊相鄰元素的加權(quán)平均值辖源,因此利用線性采樣的這個特性蔚携,可以把原本2個像素的采樣,減少為一次采樣克饶。 如果原本33次采樣酝蜒,則可以減少到17次。
對于兩個紋素的采樣矾湃,需要調(diào)整坐標使其與紋素#1中心的距離等于紋素#2的權(quán)重除以兩個權(quán)重之和亡脑。同樣的,坐標與紋素#2中心的距離應該等于紋素#1的權(quán)重除以兩個權(quán)重之和邀跃。
然后我們就有了計算線性采樣高斯濾波的權(quán)重和位移公式:
代碼講解
- 首先定義一個uniform變量霉咨,該變量表示是否啟用線性采樣的方法:
uniform bool uUseLinear;
- 然后如果使用線性采樣,就把原本的采樣次數(shù)減少一半:
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%的渲染效率。
總結(jié)
通過線性采樣的機制矮男,我們可以看到效率提高了近一倍。這在一些對性能要求高得場景或者移動終端是很有意義室谚。
其實要做出一個好的發(fā)光效果毡鉴,涉及到相關(guān)算法是很多了,而且細節(jié)之處都需要關(guān)注秒赤。
先看看我們已經(jīng)做了得一些發(fā)光樓宇得案例吧, 以下都是再簡單模型(立方體) + 貼圖 + 光照 + 發(fā)光 出來得效果猪瞬,如果模型層面在優(yōu)化,應該還可以有更酷效果:
如果對可視化感興趣入篮,可以和我交流陈瘦,微信541002349. 另外關(guān)注公眾號“ITMan彪叔” 可以及時收到更多有價值的文章。
參考文檔
參考文檔:http://rastergrid.com/blog/2010/09/efficient-gaussian-blur-with-linear-sampling/
本文部分素材使用了參考文檔中的內(nèi)容潮售。