徑向模糊,是一種從中心向外呈幅射狀,逐漸模糊的效果。 所以徑向模糊常常會產生一些中心的發散效果,在PS中一樣也有徑向模糊的濾鏡效果。 徑向模糊一般也稱爲變焦模糊。徑向模糊(Radial Blur)能夠給畫面帶來很好的速度感,是各種遊戲中後處理的常客,也經常使用於Sun Shaft等後處理特效中做爲光線投射(體積光)的模擬。web
在遊戲中,經常使用徑向模糊來模擬一些運動的動感效果。如鬼泣4中的場景切換特效,和一些技能打擊特效;賽車遊戲也嘗用來模擬動感模糊,如狂野飆車,極品飛車等。 徑向模糊仍是實現體積光照的一種技術手段之一,以下圖:算法
圖形學中模糊的大體原理都是同樣的:就是從原像素周圍去尋找附近像素的顏色,來共同影響當前的像素顏色。如高斯模糊就是將原像素四周像素的顏色加權求和做爲原像素的顏色以達到模糊的目的。canvas
不一樣的模糊就是取周邊像素和加權求和的算法不太同樣。徑向模糊的特色是從某個中心點向外散射擴散,所以其須要採樣的像素來自於當前的像素點和中心點的連線上,經過參數能夠控制採樣的數量和採樣步進的距離。像素的顏色是由該像素的點與中心點之間連線上進行採樣,而後求將這些採樣點顏色的加權平均。根據徑向模糊的特性,離目標點越近採樣點越密集,反之亦然。dom
所以,實現徑向模糊的大體流程以下:函數
徑向模糊是一個後處理過程,徑向模糊能夠對靜態的圖片施加效果,也能夠對動態渲染的圖像施加效果。本示例中將對動態的圖像施加效果。先上一張圖看看效果:
性能
首先繪製的幾個圓環對象,而後對繪製的圖像施加徑向模糊。webgl
要施加徑向模糊,首先要把圓環繪製到texture對象上面,咱們知道,經過framebuffer的技術,能夠實現把繪製效果輸出到貼圖對象上。
有關framebuffer的技術,不屬於本文重點介紹的內容,若是讀者不熟悉,能夠自行查找相關資料。也能夠參考:渲染到紋理。code
要把貼圖對象輸出到屏幕上面,咱們須要構造一個矩形對象,該對象正好是webgl座標系中的四個頂點,代碼以下:orm
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專欄,完整代碼能夠在專欄中獲取:
https://xiaozhuanlan.com/topic/6480975213
下一篇文章講述利用徑向模糊實現體積光的效果,先上一張圖看看效果:
案例視頻 能夠關注視頻號 "ITman彪叔"觀看,也歡迎關注公衆號。