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彪叔
上一篇:金剛石複合片的應用和效能特點