webgl智慧樓宇發光系列之線性採樣下高斯模糊

[toc]web

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中僅支持有限數量的紋理提取。webgl

線性採樣

到此,咱們知道了把一個二維的高斯模糊 分離成兩個一維的高斯模糊。效率上也有了大幅度的提升。可是實際上,咱們還能夠經過線性採樣的特性進一步提升效率。spa

咱們知道,要獲取一個像素信息,就要作一次貼圖的讀取。這就意味33個像素信息,就須要作33次貼圖的讀取操做。 可是因爲在GPU上面能夠隨意進行雙線線性插值,而沒有額外的性能消耗。  這就意味着,若是咱們再也不像素的中心點讀取貼圖,就能夠得到多個像素的信息。  以下圖所示:
code

假設兩個像素,咱們在像素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/本文部分素材使用了參考文檔中的內容。

相關文章
相關標籤/搜索