webgl實現徑向模糊


徑向模糊簡介

徑向模糊,是一種從中心向外呈幅射狀,逐漸模糊的效果。 因此徑向模糊經常會產生一些中心的發散效果,在PS中同樣也有徑向模糊的濾鏡效果。 徑向模糊通常也稱為變焦模糊。徑向模糊(Radial Blur)可以給畫面帶來很好的速度感,是各類游戲中后處理的常客,也常用於Sun Shaft等后處理特效中作為光線投射(體積光)的模擬。

在游戲中,常常使用徑向模糊來模擬一些運動的動感效果。如鬼泣4中的場景切換特效,和一些技能打擊特效;賽車游戲也嘗用來模擬動感模糊,如狂野飆車,極品飛車等。 徑向模糊還是實現體積光照的一種技術手段之一,如下圖:

《巫師2》 中基於徑向模糊(Radial Blur)的Sun Shaft

徑向模糊的原理

圖形學中模糊的大致原理都是一樣的:就是從原像素周圍去尋找附近像素的顏色,來共同影響當前的像素顏色。如高斯模糊就是將原像素四周像素的顏色加權求和作為原像素的顏色以達到模糊的目的。

不同的模糊就是取周邊像素和加權求和的算法不太一樣。徑向模糊的特點是從某個中心點向外散射擴散,因此其需要采樣的像素來自於當前的像素點和中心點的連線上,通過參數可以控制采樣的數量和采樣步進的距離。像素的顏色是由該像素的點與中心點之間連線上進行采樣,然后求將這些采樣點顏色的加權平均。根據徑向模糊的特性,離目標點越近采樣點越密集,反之亦然。

因此,實現徑向模糊的大致流程如下:

  • 確定徑向模糊的中心點,一般為畫布的中心點,或這個某個對象的中心點在屏幕投影所在的位置。(注意中心點是2維坐標)
  • 計算當前像素和中心點的距離和向量線段。
  • 在線段上面進行采樣,加權。
  • 將模糊的結果和原圖進行一個疊加合成(可能需要)

webgl實現徑向模糊

徑向模糊是一個后處理過程,徑向模糊可以對靜態的圖片施加效果,也可以對動態渲染的圖像施加效果。本示例中將對動態的圖像施加效果。先上一張圖看看效果:
webgl徑向模糊

首先繪制的幾個圓環對象,然后對繪制的圖像施加徑向模糊。

渲染到紋理

要施加徑向模糊,首先要把圓環繪制到texture對象上面,我們知道,通過framebuffer的技術,可以實現把繪制效果輸出到貼圖對象上。
有關framebuffer的技術,不屬於本文重點介紹的內容,如果讀者不熟悉,可以自行查找相關資料。也可以參考:渲染到紋理

輸入貼圖對象

要把貼圖對象輸出到屏幕上面,我們需要構造一個矩形對象,該對象正好是webgl坐標系中的四個頂點,代碼如下:

function quad() {
  var pos = [-1,1,0, -1,-1,0, 1,-1,0, 1,1,0],
    st = [0,1,0,0,1,0, 1,1],
    idx = [0,1,2,0,2,3];

  return {
    p:pos,
    t:st,
    i:idx,
  }
}

上述對象可以正好把一個貼圖對象完整的輸出到屏幕上(webgl坐標系)

實現徑向模糊

徑向模糊的主要在着色器語言中進行實現,而且主要是在片元着色器中,下面是片元着色器的代碼:

var ofs = `precision mediump float;
uniform sampler2D texture;
uniform float strength;
varying vec2 vTexCoord;

const float tFrag = 1.0 / 512.0;
const float nFrag = 1.0 / 30.0;
const vec2 centerOffset = vec2(256.0, 256.0);

float rnd(vec3 scale, float seed) {
  return fract(sin(dot(gl_FragCoord.stp + seed, scale)) * 43758.5453 + seed);
}

vec4 fragRadialBlur(){
  vec2 fc = vec2(gl_FragCoord.s, 512.0 - gl_FragCoord.t);
  vec2 fcc = fc - centerOffset;
  vec3 destColor = vec3(0.0);
  float random = rnd(vec3(12.9898, 78.233, 151.7182), 0.0);
  float totalWeight = 0.0;
  for (float i = 0.0; i <= 30.0; i++) {
    float percent = (i + random) * nFrag;
    float weight = percent - percent * percent;
    vec2 t = fc - fcc * percent * strength * nFrag;
    destColor += texture2D(texture, t * tFrag).rgb * weight;
    totalWeight += weight;
  }
  return vec4(destColor / totalWeight, 1.0);
}

void main(void) {
  gl_FragColor =  fragRadialBlur();
  
}`;

隨機函數

首先聲明了一個rnd函數,此函數是一個簡單的隨機數生成器,當給定向量和種子值時,將返回0到1范圍內的隨機數。
使用隨機數,是為了讓模糊的效果呈現一定的隨機狀態,而不是按照某種一致性的原則進行模糊。
rnd 在每次片元着色器中都會調用,因此要盡量使用輕量化的實現,不然可能會造成性能負載。

定義相關變量

const float tFrag = 1.0 / 512.0;
const float nFrag = 1.0 / 30.0;
const vec2  centerOffset = vec2(256.0, 256.0);

然后定義相關變量。在此示例中,縮放的中心點設置為畫布的中心。
畫布的大小為512像素,因此上面的代碼相應地聲明了一些常量。
vec2變量centerOffset用於定義中心位置。
floag變量tFrag用於規范化,把二維頂點坐標轉換成歸一化為uv坐標,以正確引用着色器中的紋理像素。
另一個float類型常量nFrag用於着色器中for的語句進行迭代處理進行歸一化。

徑向模糊邏輯

vec4 fragRadialBlur(){
  vec2 fc = vec2(gl_FragCoord.s, 512.0 - gl_FragCoord.t);
  vec2 fcc = fc - centerOffset;
  vec3 destColor = vec3(0.0);
  float random = rnd(vec3(12.9898, 78.233, 151.7182), 0.0);
  float totalWeight = 0.0;
  for (float i = 0.0; i <= 30.0; i++) {
    float percent = (i + random) * nFrag;
    float weight = percent - percent * percent;
    vec2 t = fc - fcc * percent * strength * nFrag;
    destColor += texture2D(texture, t * tFrag).rgb * weight;
    totalWeight += weight;
  }
  return vec4(destColor / totalWeight, 1.0);
}

首先,通過gl_FragCoord計算出變量fc,表示當前像素在canvas畫布的坐標。 注意gl_FragCoord坐標的原點是在左下角,而canvas畫布的坐標原點在左上角,應此做了一個翻轉計算:

512.0 - gl_FragCoord.t

計算變量fcc,表示當前坐標到中心點的向量。

定義變量destColor用於保存最終要輸出的像素顏色。 然后計算一個隨機值random。totalWeight表示每次迭代時候采樣像素所占的比重之和。

然后開始迭代處理。

在片段着色器中通過for語句進行迭代處理,使用i加上隨機數之和來計算目標像素的percent(比重),然后通過percent - percent * percent 是為了把線性的比重數據變成非線性的比重weight。
然后通過percent和strength計算出一采樣的坐標點t,其中strength表示徑向模糊的強度,此值越大,偏離越大,模糊效果越強。並通過texture2D函數獲取對應坐標點的顏色值,然后乘以weight並加入到destColor,並最終計算totalWeight。

最后,完成迭代過程后進行歸一化destColor: destColor / totalWeight。

說明

此處我們使用了30次迭代,看起來性能並沒有太大影響。實際過程中,可以選擇不同的迭代次數,來達到效果和性能的平衡。

最終效果如下,
webgl徑向模糊

本文也發表在我的webgl專欄,完整代碼可以在專欄中獲取:

https://xiaozhuanlan.com/topic/6480975213

下一篇文章講述利用徑向模糊實現體積光的效果,先上一張圖看看效果:
體積光

案例視頻 可以關注視頻號 "ITman彪叔"觀看,也歡迎關注公眾號。
ITman彪叔公眾號


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM