后處理(Post-Processing),在圖形學和游戲開發等領域是提升最終畫面呈現品質的重要渲染技術。后處理渲染技術的好壞,往往決定了游戲畫面是否能夠達到令人驚艷的級別。
圖像模糊算法在后處理渲染領域中占據着重要的地位。很多產品級后處理的實現,都會直接或間接依賴於圖像模糊算法中的一種或多種。無論是基於高斯模糊(Gaussian Blur)或其改進算法的Bloom特效,還是基於徑向模糊(Radial Blur)的Sun Shaft(God Ray),或是基於方向模糊(Directional Blur)的鏡頭眩光光暈(Glare Lens Flare),抑或是景深(Depth of Field)特效中攝影級失焦感的散景模糊(Bokeh Blur),都以模糊算法作為重要的支撐。所以說,后處理中所采用模糊算法的優劣,決定了后處理管線最終的渲染品質和消耗性能的多少。
本文將對后處理管線中會使用到的如下十種模糊算法進行總結、對比和盤點,以及提供了這十種模糊算法以及其衍生算法對應的Unity Post Processing Stack v2版本的實現:
- 高斯模糊(Gaussian Blur)
- 方框模糊(Box Blur)
- Kawase模糊(Kawase Blur)
- 雙重模糊(Dual Blur)
- 散景模糊(Bokeh Blur)
- 移軸模糊(Tilt Shift Blur)
- 光圈模糊(Iris Blur)
- 粒狀模糊(Grainy Blur)
- 徑向模糊(Radial Blur)
- 方向模糊(Directional Blur)
另外,本文暫不涉及運動模糊(Motion Blur),因為其主要作用於幀之間的運動變化,不屬於靜態型模糊。還有一些其它的模糊算法由於不太適用於實時渲染,本文也暫不涉及,如:Moving Averages filter。
下面先放一組使用了依賴於模糊算法的后處理特效的實時渲染截圖,然后開始我們的正文。

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

UE4場景中,間接基於高斯模糊(Gaussian Blur)的Bloom特效給畫面帶了更好的光感 @ UE4 CyberNeon @Junliang Zhang

Sun Shaft Forest @UE4

《鬼泣5》中的鏡頭眩光光暈(Glare Lens Flare)

Tom Clancys The Division中的基於散景模糊(Bokeh Blur)的景深
這里也放一個《賽博朋克:霓虹中國(CyberNeon)》的視頻(ArtStation原貼:https://www.artstation.com/artwork/Z5RkbZ),其中對於賽博朋克風夜中國風城市的展現,如果缺少了Bloom和Glare Lens Flare等依賴於本文講到的模糊算法的后處理特效,展現出來的品質將少了很多韻味。
十種圖像模糊算法橫向對比
在展開全文,對這十種圖像模糊算法進行分別介紹之前,這一節中先做一個總覽,即一個橫向的對比。要評判一種模糊算法的好壞,主要有三個標准:
- 模糊品質(Quality):模糊品質的好壞是模糊算法是否優秀的主要指標。
- 模糊穩定性(Stability):模糊的穩定性決定了在畫面變化過程中,模糊是否穩定,不會出現跳變或者閃爍。
- 性能(Performance):性能的好壞是模糊算法是否能被廣泛使用的關鍵所在。
以下是本文涉及的十種模糊算法在標准情況下以上述三個指標作為評判標准的橫向對比:

從上表的對比可以看到,除了Grainy Blur因其模糊質感的特殊性獲得了“一般”的模糊品質評級之外,另外九種模糊算法在模糊品質和穩定性這兩方面都獲得了不錯的評級。這是因為給到足夠的迭代次數,且不做RenderTexture的DownSample,它們都可以收斂到一個高品質的模糊質感。
最終的分化在於性能,這才是評判一種算法性價比是否高,能否廣泛用於實時渲染的關鍵因素。其中,可以看到僅雙重模糊(Dual Blur)和粒狀模糊(Grainy Blur)兩種算法,獲得了高的性能評級。當然,這是針對標准的算法而言,其它八種算法如果進行進一步的性能優化,也能具有更佳的性能。
關於X-PostProcessing Libray
X-PostProcessing Libray,簡稱XPL,是本人開發的Unity引擎下的高品質開源后處理算法庫,旨在提供業界主流的高品質后處理特效的完整解決方案,目前已完美支持Unity Post-processing Stack v2,后續也將提供對Unity引擎URP/LWRP/HDRP的兼容支持。
【GitHub地址】:https://github.com/QianMo/X-PostProcessing-Library

截止本文發表,目前已以開源形式放出了17種Blur算法的后處理實現。而隨着后續更多內容的公開,X-PostProcessing Libray將成型為一個具有100+種后處理特效的高品質后處理開源算法庫。
下面我們開始正文。先從最熱門,最為大眾所熟知的高斯模糊開始。
一、高斯模糊(Gaussian Blur)
高斯模糊(Gaussian Blur),也叫高斯平滑(Gaussian Smoothing),作為最經典的模糊算法,一度成為模糊算法的代名詞。

高斯模糊在圖像處理領域,通常用於減少圖像噪聲以及降低細節層次,以及對圖像進行模糊,其視覺效果就像是經過一個半透明屏幕在觀察圖像。
從數字信號處理的角度看,圖像模糊的本質一個過濾高頻信號,保留低頻信號的過程。過濾高頻的信號的一個常見可選方法是卷積濾波。從這個角度來說,圖像的高斯模糊過程即圖像與正態分布做卷積。由於正態分布又叫作“高斯分布”,所以這項技術就叫作高斯模糊。而由於高斯函數的傅立葉變換是另外一個高斯函數,所以高斯模糊對於圖像來說就是一個低通濾波器。
用於高斯模糊的高斯核(Gaussian Kernel)是一個正方形的像素陣列,其中像素值對應於2D高斯曲線的值。

一個典型的高斯核
圖像中的每個像素被乘以高斯核,然后將所有這些值相加,得到輸出圖像中此處的值。

N維空間高斯模糊方程可以表示為:

在二維空間定義為:

其中為模糊半徑。
下圖為高斯函數的三維圖示:

高斯模糊也可以在二維圖像上對兩個獨立的一維空間分別進行計算,即滿足線性可分(Linearly separable)。這也就是說,使用二維矩陣變換得到的效果也可以通過在水平方向進行一維高斯矩陣變換加上豎直方向的一維高斯矩陣變換得到。從計算的角度來看,這是一項有用的特性,因為這樣只需要 的計算復雜度,而原先的計算復雜度為
,其中M、N是需要進行濾波的圖像的維數,m、n是濾波器的維數。
以下為一個Gaussian Kernel的線性分解過程:

而下圖很好地對Gaussian Kernel的線性可分進行了描述:

實現方面,可以采用經過線性分解的高斯核的方案,且用乒乓RenderTexture交互blit的方法。高斯模糊對應的Fragment Shader的實現為:
float4 FragGaussianBlur(v2f i): SV_Target { half4 color = float4(0, 0, 0, 0); color += 0.40 * SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, i.uv); color += 0.15 * SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, i.uv01.xy); color += 0.15 * SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, i.uv01.zw); color += 0.10 * SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, i.uv23.xy); color += 0.10 * SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, i.uv23.zw); color += 0.05 * SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, i.uv45.xy); color += 0.05 * SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, i.uv45.zw); return color; }
篇幅原因,在這里以及后文中,對應后處理的Runtime + Shader的完整的實現就不貼了,但會給出XPL中的實現鏈接。
完整高斯模糊的Runtime + Shader實現可見:
以下是開啟高斯模糊后處理前后的對比圖:


以及展示了BlurRadius為3,Iteration為6,RT DownScale為1的設置下,經過橫縱線性分解的高斯模糊的渲染過程的動圖:

對模糊半徑(Blur Radius)參數的調節,可以控制高斯模糊的程度:

二、方框模糊(Box Blur)
方框模糊(Box Blur),又常被稱為盒式模糊,其中所得到的圖像中的每個像素具有的值等於其鄰近的像素的輸入圖像中的平均值。和高斯模糊一樣,Box Blur也是低通濾波器的一種形式。在圖像處理領域,Box Blur通常用於近似高斯模糊。因為根據中心極限定理,重復應用Box Blur可以得到和高斯模糊非常近似的模糊表現。
可以將3 x 3的Box Blur的kernel表示為如下矩陣:

而2 x 2的Box Blur的kernel表示為如下矩陣:

Box Blur和高斯模糊的性質對比可見下圖:

3D結構,2D結構和示例矩陣對比(a)Box Blur(b)Gaussian Blur
以下是Box Blur的作用過程的總結:

Box Blur也是線性可分的,如有需要,也可以借助其此性質,如下所示:

另外Box Blur也有不少擴展與變體,比如Tent Blur(兩次Box Blur)、Quadratic Blur(三次Box Blur)等,具體本文暫時就不展開了。
其中,Tent Blur也已在XPL中進行了實現,具體可見:
https://github.com/QianMo/X-PostProcessing-Library/tree/master/Assets/X-PostProcessing/Effects/TentBlur
以下是一個4 x 4的Box Filter的Shader實現,Low Level Optimize方面,可以采用乘加組合的書寫方式,即MAD指令友好的形式,以在部分GPU上實現指令數的優化:
half4 BoxFilter_4Tap(TEXTURE2D_ARGS(tex, samplerTex), float2 uv, float2 texelSize) { float4 d = texelSize.xyxy * float4(-1.0, -1.0, 1.0, 1.0); half4 s = 0; s = SAMPLE_TEXTURE2D(tex, samplerTex, uv + d.xy) * 0.25h; // 1 MUL s += SAMPLE_TEXTURE2D(tex, samplerTex, uv + d.zy) * 0.25h; // 1 MAD s += SAMPLE_TEXTURE2D(tex, samplerTex, uv + d.xw) * 0.25h; // 1 MAD s += SAMPLE_TEXTURE2D(tex, samplerTex, uv + d.zw) * 0.25h; // 1 MAD return s; }
Box Blur完整的Runtime + Shader實現可見:
https://github.com/QianMo/X-PostProcessing-Library/tree/master/Assets/X-PostProcessing/Effects/BoxBlur
另外也可以考慮將UV計算放到Vertex層。
Box Blur的渲染效果接近高斯模糊,但性價比並不高,需要較多的迭代次數才能達到高品質的模糊效果:

以下是Box Blur在BlurRadius為1.6,Iteration為6,RTDownScale為1的設置下模糊過程的動圖:

三、Kawase模糊(Kawase Blur)
Kawase Blur於Masaki Kawase 在GDC 2003的分享《Frame Buffer Postprocessing Effects in DOUBLE-S.T.E.A.L (Wreckless)》中提出。Kawase Blur最初用於Bloom后處理特效,但其可以推廣作為專門的模糊算法使用,且在模糊外觀表現上與高斯模糊非常接近。Kawase Blur的思路是對距離當前像素越來越遠的地方對四個角進行采樣,且在兩個大小相等的紋理之間進行乒乓式的blit。創新點在於,采用了隨迭代次數移動的blur kernel,而不是類似高斯模糊,或Box Blur一樣從頭到尾固定的blur kernel。


實踐數據表明,在相似的模糊表現下,Kawase Blur比經過優化的高斯模糊的性能約快1.5倍到3倍。
具體思路是在Runtime層,基於當前迭代次數,對每次模糊的半徑進行設置,而Shader層實現一個4 tap的Kawase Filter即可:
half4 KawaseBlur(TEXTURE2D_ARGS(tex, samplerTex), float2 uv, float2 texelSize, half pixelOffset) { half4 o = 0; o += SAMPLE_TEXTURE2D(tex, samplerTex, uv + float2(pixelOffset +0.5, pixelOffset +0.5) * texelSize); o += SAMPLE_TEXTURE2D(tex, samplerTex, uv + float2(-pixelOffset -0.5, pixelOffset +0.5) * texelSize); o += SAMPLE_TEXTURE2D(tex, samplerTex, uv + float2(-pixelOffset -0.5, -pixelOffset -0.5) * texelSize); o += SAMPLE_TEXTURE2D(tex, samplerTex, uv + float2(pixelOffset +0.5, -pixelOffset -0.5) * texelSize); return o * 0.25; }
完整的Runtime + Shader實現可見:
Kawase Blur渲染效果接近高斯模糊,但具有更好的性能:

以下是在初始RT DownScale為1、Iteration為6的設置下,Dual Kawase Blur的渲染步驟:

同樣,對模糊半徑(Blur Radius)參數的調節,可以控制Kawase模糊的程度:

四、雙重模糊(Dual Blur)
Dual Kawase Blur,簡稱Dual Blur,是SIGGRAPH 2015上ARM團隊提出的一種衍生自Kawase Blur的模糊算法。其由兩種不同的Blur Kernel構成,如下圖所示。

相較於Kawase Blur在兩個大小相等的紋理之間進行乒乓blit的的思路,Dual Kawase Blur的核心思路在於blit過程中進行降采樣和升采樣,即對RT進行了降采樣以及升采樣。如下圖所示:

由於靈活的升降采樣帶來了blit RT所需計算量的減少等原因, Dual Kawase Blur相較於上文中提到的Gauusian Blur、Box Blur、Kawase Blur等Blur算法,有更好的性能,下圖是相同條件下的性能對比。

可以看到,Dual Kawase Blur具有最佳的性能表現。
為了帶來更好的性能表現,可以將UV的偏移放在Vert Shader中進行,而Fragment Shader中基本上僅進行采樣即可。
Dual Kawase Blur的Fragment Shader實現為:
half4 Frag_DownSample(v2f_DownSample i): SV_Target { half4 sum = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, i.uv) * 4; sum += SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, i.uv01.xy); sum += SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, i.uv01.zw); sum += SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, i.uv23.xy); sum += SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, i.uv23.zw); return sum * 0.125; } half4 Frag_UpSample(v2f_UpSample i): SV_Target { half4 sum = 0; sum += SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, i.uv01.xy); sum += SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, i.uv01.zw) * 2; sum += SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, i.uv23.xy); sum += SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, i.uv23.zw) * 2; sum += SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, i.uv45.xy); sum += SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, i.uv45.zw) * 2; sum += SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, i.uv67.xy); sum += SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, i.uv67.zw) * 2; return sum * 0.0833; }
完整的Runtime + Shader實現可見:
XPL中也提供了啟發自Dual Kawase Blur的Dual Gaussian Blur、Dual Box Blur、Dual Tent Blur的實現。
Dual Gaussian Blur: https://github.com/QianMo/X-PostProcessing-Library/tree/master/Assets/X-PostProcessing/Effects/DualGaussianBlur
Dual Box Blur: https://github.com/QianMo/X-PostProcessing-Library/tree/master/Assets/X-PostProcessing/Effects/DualBoxBlur
Dual Tent Blur: https://github.com/QianMo/X-PostProcessing-Library/tree/master/Assets/X-PostProcessing/Effects/DualTentBlur
Dual Kawase Blur最終的模糊效果截圖如下,可以看到其與高斯模糊的模糊表現也非常接近:

以下是在初始RT DownScale為1、Iteration為5的設置下,Dual Kawase Blur的渲染步驟:

同樣,對模糊半徑(Blur Radius)參數的調節,可以控制Dual Kawase Blur模糊的程度:

五、散景模糊(Bokeh Blur)
散景(Bokeh)亦稱焦外成像,是一個攝影名詞,一般表示在景深較淺的攝影成像中,落在景深以外的畫面,會有逐漸產生松散模糊的效果。散景效果有可能因為攝影技巧或光圈孔形狀的不同,而產生各有差異的效果。例如:鏡頭本身的光圈葉片數不同(所形成的光圈孔形狀不同),會讓圓形散景呈現不同的多角形變化。此外,反射式鏡頭焦外的散景,會呈現獨有的甜甜圈形狀。

不同相機參數下得到的不同散景模糊(Bokeh Blur)
散景(Bokeh)在攝影學中被稱為焦外成像,而在光學上被稱為Circle of Confusion, CoC(彌散圓/散光圈/散射圓盤 ),即下圖橙色Image Plane中的藍色C所示區域。由於不同的物距(物體到鏡頭的距離)投影到鏡頭所形成的焦點不同,但Image Plane只能放在某個點上,所以就形成了Circle of Confusion, CoC(彌散圓)。

散景(Bokeh)成因 (圖片來自GPU Gems 1)

散景(Bokeh)大小不同的成因,即Circle of Confusion, CoC(彌散圓)的大小與人眼分辨率不同的區域。
鏡頭本身的光圈葉片數不同(所形成的光圈孔形狀不同),會讓散景形狀呈現不同的多角形變化。從最初的多邊形,過渡到最終的圓形。

不同光圈葉片數的鏡頭,決定了不同的散景形狀

不同光圈葉片數的鏡頭,決定了不同的散景形狀

不同光圈數值的鏡頭形態,決定了不同的散景形態
從上圖可以看出, 由於光圈大小和葉片數量的不同,散景(Bokeh)的形態各異。
在圖形學領域模擬散景(Bokeh)的方法有很多,本文將以最標准的圓形散景為例,采用Golden angle的思路進行散景模糊(Bokeh Blur)算法的實現。
具體而言,算法思路是基於對大量離散螺旋型(spiral)分布的點的渲染,來模擬出圓形Bokeh的形狀。

核心的Shader算法實現為:
half4 BokehBlur(VaryingsDefault i) { half2x2 rot = half2x2(_GoldenRot); half4 accumulator = 0.0; half4 divisor = 0.0; half r = 1.0; half2 angle = half2(0.0, _Radius); for (int j = 0; j < _Iteration; j++) { r += 1.0 / r; angle = mul(rot, angle); half4 bokeh = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, float2(i.texcoord + _PixelSize * (r - 1.0) * angle)); accumulator += bokeh * bokeh; divisor += bokeh; } return accumulator / divisor; }
即對於每一次迭代,讓采樣UV旋轉一個角度,經過足夠次數的迭代后,眾多以圓形分散開來的點,將一起組成合適的Bokeh形狀。
完整的Runtime + Shader實現可見:
下圖為最終實現的效果圖:

不同模糊半徑(Blur Radius)變化,可以控制不同的Bokeh半徑的變化:

六、移軸模糊 (Tilt Shift Blur)
移軸模糊(Tilt Shift Blur),又稱鏡頭模糊(Lens Blur) ,是源自攝影領域的一種模糊算法。
在攝影領域,移軸攝影(Tilt-Shift Photography)泛指利用移軸鏡頭創作的作品,所拍攝的照片效果就像是縮微模型一樣,非常特別。移軸鏡頭的作用,本來主要是用來修正以普通廣角鏡拍照時所產生出的透視問題,但后來卻被廣泛利用來創作變化景深聚焦點位置的攝影作品。移軸鏡攝影是將真實世界拍成像假的一樣,使照片能夠充分表現出“人造都市”的感覺。

移軸攝影作品
在后處理渲染中進行移軸攝影的模擬,可以采用Grident UV算法控制畫面區域模糊強度,配合全屏模糊算法的方式來實現。
采用Grident UV算法控制畫面區域模糊強度的Shader核心實現如下:
float TiltShiftMask(float2 uv) { float centerY = uv.y * 2.0 - 1.0 + _Offset; // [0,1] -> [-1,1] return pow(abs(centerY * _Area), _Spread); }
得到的屏幕模糊強度的mask圖如下:

接着,配合合適的全屏圖像模糊算法,如Bokeh Blur,便可以營造出移軸攝影的畫面感:

完整的Runtime + Shader實現可見:
https://github.com/QianMo/X-PostProcessing-Library/tree/master/Assets/X-PostProcessing/Effects/TiltShiftBlurV2
這里也有了另一個版本的實現:
https://github.com/QianMo/X-PostProcessing-Library/tree/master/Assets/X-PostProcessing/Effects/TiltShiftBlur
對模糊半徑(Blur Radius)參數的調節,可以用於控制移軸Bokeh半徑的變化:

在一定的區域平滑度(Area Smooth)設置下,調節區域尺寸(Area Size)可以控制移軸模糊區域的變化:

七、光圈模糊(Iris Blur)
光圈模糊(Iris Blur)是Photoshop CS6中新增的功能,用於模擬淺景深的效果。
可以根據用戶不同的輸入參數,將普通照片模擬出景深以及散景的效果。
PS:Photoshop中也同樣有Tilf-Shift Blur功能

Photoshop中的光圈模糊(Iris Blur)功能

Photoshop中的光圈模糊(Iris Blur)功能
在后處理渲染中進行光圈模糊的模擬,可以采用一個徑向的Grident UV算法沿軸心控制畫面區域模糊強度,並配合全屏模糊算法的方式來實現。
采用徑向Grident UV算法控制畫面區域模糊強度的Shader核心實現如下:
float IrisMask(float2 uv) { float2 center = uv * 2.0 - 1.0 + _Offset; // [0,1] -> [-1,1] return dot(center, center) * _AreaSize; }
得到的屏幕模糊強度的mask圖如下:

同樣,配合合適的全屏圖像模糊算法,如Bokeh Blur,便可以營造出移軸攝影的畫面感:

光圈模糊(Iris Blur)完整的Runtime + Shader實現可見:
https://github.com/QianMo/X-PostProcessing-Library/tree/master/Assets/X-PostProcessing/Effects/IrisBlurV2
這里也有另一個版本的實現:
https://github.com/QianMo/X-PostProcessing-Library/tree/master/Assets/X-PostProcessing/Effects/IrisBlur
對模糊半徑(Blur Radius)參數的調節,可以用於控制光圈Bokeh半徑的變化:

同樣,調節區域尺寸(Area Size)可以控制光圈模糊區域的變化:

八、粒狀模糊(Grainy Blur)
粒狀模糊(Grainy Blur)是一種低成本的模糊方法,在單pass下即可有合適的模糊表現,性能出色,且其模糊質感有點類似在畫面上蒙了一層細碎的冰霜。

其思路是基於隨機UV進行采樣的抖動,以對粗粒度的模糊進行模擬。核心算法的Shader實現如下:
float Rand(float2 n) { return sin(dot(n, half2(1233.224, 1743.335))); } half4 GrainyBlur(VaryingsDefault i) { half2 randomOffset = float2(0.0, 0.0); half4 finalColor = half4(0.0, 0.0, 0.0, 0.0); float random = Rand(i.texcoord); for (int k = 0; k < int(_Iteration); k ++) { random = frac(43758.5453 * random + 0.61432);; randomOffset.x = (random - 0.5) * 2.0; random = frac(43758.5453 * random + 0.61432); randomOffset.y = (random - 0.5) * 2.0; finalColor += SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, half2(i.texcoord + randomOffset * _BlurRadius)); } return finalColor / _Iteration; }
這邊是一個模糊半徑更大的粒狀模糊(Grainy Blur)的渲染效果:

粒狀模糊(Grainy Blur)完整的Runtime + Shader實現可見:
對模糊半徑(Blur Radius)參數的調節,也可以控制粒狀模糊的程度:

九、徑向模糊(Radial Blur)
徑向模糊(Radial Blur)可以給畫面帶來很好的速度感,是各類游戲中后處理的常客,也常用於Sun Shaft等后處理特效中作為光線投射的模擬。

徑向模糊的原理比較直接,首先選取一個徑向軸心(Radial Center),然后將每一個采樣點的UV基於此徑向軸心進行偏移(Offset),並進行一定次數的迭代采樣,最終將采樣得到的RGB值累加,並除以迭代次數。
其核心算法的Shader代碼實現如下:
half4 RadialBlur(VaryingsDefault i) { float2 blurVector = (_RadialCenter - i.texcoord.xy) * _BlurRadius; half4 acumulateColor = half4(0, 0, 0, 0); [unroll(30)] for (int j = 0; j < _Iteration; j ++) { acumulateColor += SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, i.texcoord); i.texcoord.xy += blurVector; } return acumulateColor / _Iteration; }
完整的Runtime + Shader實現可見:
另外需要注意的是,如果迭代次數不夠多,而又設置了較高的Offset值,則會在屏幕四周出現較為明顯的折痕,但一般情況下都還可以接受:

同樣,對模糊半徑(Blur Radius)參數的調節,可以控制模糊的程度:

十、方向模糊(Directional Blur)
方向模糊(Directional Blur)可以看做是徑向模糊(Radial Blur)的一個變體。其主要思路是傳入一個角度,然后在runtime層計算出對應的矢量方向:
float sinVal = (Mathf.Sin(settings.Angle) * settings.BlurRadius * 0.05f) / settings.Iteration; float cosVal = (Mathf.Cos(settings.Angle) * settings.BlurRadius * 0.05f) / settings.Iteration; sheet.properties.SetVector(ShaderIDs.Direction, new Vector2(sinVal, cosVal));
然后,在Shader層,將每一個采樣點的UV基於此方向進行正負兩次偏移(Offset),接着進行一定次數的迭代采樣,最終將采樣得到的RGB值累加,並除以迭代次數,得到最終的輸出。
核心算法的Shader代碼實現如下:
half4 DirectionalBlur(VaryingsDefault i) { half4 color = half4(0.0, 0.0, 0.0, 0.0); for (int k = -_Iteration; k < _Iteration; k++) { color += SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, i.texcoord - _Direction * k); } half4 finalColor = color / (_Iteration * 2.0); return finalColor; }
完整的Runtime + Shader實現可見:
https://github.com/QianMo/X-PostProcessing-Library/tree/master/Assets/X-PostProcessing/Effects/DirectionalBlur
方向模糊后處理的渲染效果如下:

同樣,對模糊半徑(Blur Radius)參數的調節,可以控制模糊的程度:

而調節Angle參數,可以控制模糊旋轉的方向:

總結
本文開頭已經提到,模糊算法在后處理渲染領域占據着非常重要的地位。很多產品級后處理的實現,都會直接或間接依賴於一種或多種圖像模糊算法。后處理管線中所采用的模糊算法的優劣,決定了產品最終的渲染品質和消耗的性能大小。
讓我們重新看一下這十種模糊算法的橫向對比,相信此時,大家對這十種模糊算法已經有了更全面的理解,以及自己的選擇。

Reference
[1] GDC 2003, Frame Buffer Postprocessing Effects in DOUBLE-S.T.E.A.L (Wreckless)
[2] SIGGRAPH 2015, Bandwidth-Efficient Rendering
[3] SIGGRAPH 2001,Fast Image Convolutions
[4] Gonzalez R C, Woods R E. Digital image processing, 4th edn. ISBN: 9780133356724[J]. 2017.
[5] https://link.zhihu.com/?target=https%3A//computergraphics.stackexchange.com/questions/39/how-is-gaussian-blur-implemented
[6] http://datahacker.rs/opencv-average-and-gaussian-filter/
[7] https://towardsdatascience.com/image-processing-class-egbe443-4-filters-aa1037676130
[8] https://www.zhihu.com/question/20813608/answer/261346592
[9] https://en.wikipedia.org/wiki/Box_blur
[10] https://en.wikipedia.org/wiki/Gaussian_blur
[11] http://blog.marmakoide.org/?p=1
[12] https://developer.nvidia.com/gpugems/gpugems/part-iv-image-processing/chapter-23-depth-field-survey-techniques
[13] https://www.flickr.com/photos/valpil58/9425151785
[14] https://en.wikipedia.org/wiki/Bokeh
[15] https://commons.wikimedia.org/wiki/File:Lenses_with_different_apertures.jpg
[16] https://improvephotography.com/29529/aperture-blades-many-best/
[17] https://www.webucator.com/how-to/how-apply-an-iris-blur-effect-adobe-photoshop.cfm
[18] 題圖來自:https://www.artstation.com/artwork/Z5RkbZ
這是侑虎科技第761篇文章,感謝作者毛星雲供稿。歡迎轉發分享,未經作者授權請勿轉載。如果您有任何獨到的見解或者發現也歡迎聯系我們,一起探討。(QQ群:793972859)