生活随笔
收集整理的這篇文章主要介紹了
12.屏幕高斯模糊
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
本系列文章由@淺墨_毛星云?出品,轉載請注明出處。??
文章鏈接:?http://blog.csdn.net/poem_qianmo/article/details/51871531
作者:毛星云(淺墨)?? ?微博:http://weibo.com/u/1723155442
本文工程使用的Unity3D版本:?5.2.1?
一、降采樣與高斯模糊的原理
?
首先梳理一下在Unity中實現高斯模糊效果需用到的幾個圖像處理的知識點,說起來也很巧,正好和之前我寫過一個關于OpenCV的系列博客里的這篇文章(http://blog.csdn.net/poem_qianmo/article/details/22745559)涉及的知識點類似。
?
?
1.1?關于圖像的降采樣
?
降采樣(Downsample)也稱下采樣(Subsample),按字面意思理解即是降低采樣頻率。對于一幅N*M的圖像來說,如果降采樣系數為k,則降采樣即是在原圖中每行每列每隔k個點取一個點組成一幅圖像的一個過程。
不難得出,降采樣系數K值越大,則需要處理的像素點越少,運行速度越快。
1.2?高斯模糊的原理
?
高斯模糊(Gaussian Blur),也叫高斯平滑,高斯濾波,其通常用它來減少圖像噪聲以及降低細節層次,常常也被用于對圖像進行模糊。
通俗的講,高斯模糊就是對整幅圖像進行加權平均的過程,每一個像素點的值,都由其本身和鄰域內的其他像素值經過加權平均后得到。高斯模糊的具體操作是:用一個模板(或稱卷積、掩模)掃描圖像中的每一個像素,用模板確定的鄰域內像素的加權平均灰度值去替代模板中心像素點的值。
高斯分布的數學表示如下:
其中,x為到像素中心的距離,σ為標準差。
?
?
高斯分布(正態分布曲線)
分條來說明一下高斯模糊的幾個要點:
- 從數學的角度來看,圖像的高斯模糊過程就是圖像與正態分布做卷積。
- 由于正態分布又叫作高斯分布,所以這項技術就叫作高斯模糊。
- 高斯模糊能夠把某一點周圍的像素色值按高斯曲線統計起來,采用數學上加權平均的計算方法得到這條曲線的色值
- 所謂"模糊",可以理解成每一個像素都取周邊像素的平均值。
- 圖像與圓形方框模糊做卷積將會生成更加精確的焦外成像效果。由于高斯函數的傅立葉變換是另外一個高斯函數,所以高斯模糊對于圖像來說就是一個低通濾波器。
高斯模糊的原理大致如此。若各位還想進一步了解,可以參考高斯模糊的wiki,以及《Real-Time Rendering 3rd》,或各種圖像處理的書籍。相關參考內容見附錄中的reference。
下面主要來一起看一下高斯模糊特效在Unity中的實現。
?
二、高斯模糊特效在Unity中的實現
Unity中的屏幕特效,通常分為兩部分來實現:
- Shader代碼實現部分
- C#/javascript代碼實現部分
?上述兩者結合起來,便可以在Unity中實現具有很強可控性和靈活性的屏幕后期特效。
下面即是從這兩個方面對高斯模糊的特效進行實現。其實現思路類似Standard Assets/Image Effect中的Blur,但是本文的實現更簡潔,有更大的可控性。
?
?
2.1?Shader代碼部分
本次的高斯模糊Shader包含逐行注釋后約200多行。
書寫思路方面,采用了3個通道(Pass)各司其職,他們分別是:
- 通道0:降采樣通道。
- 通道1:垂直方向模糊處理通道。
- 通道2:水平方向模糊處理通道。
而三個通道中共用的變量、函數和結構體的代碼位于CGINCLUDE和ENDCG之間。
以下貼出經過詳細注釋的Shader源碼:
?
[cpp]?view plaincopy print?
Shader?"Learning?Unity?Shader/Lecture?15/RapidBlurEffect"?? {?? ?????? ????Properties?? ????{?? ?????????? ????????_MainTex("Base?(RGB)",?2D)?=?"white"?{}?? ????}?? ?? ?????? ????SubShader?? ????{?? ????????ZWrite?Off?? ????????Blend?Off?? ?? ?????????? ?????????? ????????Pass?? ????????{?? ????????????ZTest?Off?? ????????????Cull?Off?? ?? ????????????CGPROGRAM?? ?? ?????????????? ????????????#pragma?vertex?vert_DownSmpl?? ?????????????? ????????????#pragma?fragment?frag_DownSmpl?? ?? ????????????ENDCG?? ?? ????????}?? ?? ?????????? ?????????? ????????Pass?? ????????{?? ????????????ZTest?Always?? ????????????Cull?Off?? ?? ????????????CGPROGRAM?? ?? ?????????????? ????????????#pragma?vertex?vert_BlurVertical?? ?????????????? ????????????#pragma?fragment?frag_Blur?? ?? ????????????ENDCG?? ????????}?? ?? ?????????? ?????????? ????????Pass?? ????????{?? ????????????ZTest?Always?? ????????????Cull?Off?? ?? ????????????CGPROGRAM?? ?? ?????????????? ????????????#pragma?vertex?vert_BlurHorizontal?? ?????????????? ????????????#pragma?fragment?frag_Blur?? ?? ????????????ENDCG?? ????????}?? ????}?? ?? ?? ?????? ????CGINCLUDE?? ?? ?????? ????#include?"UnityCG.cginc"?? ?? ?????? ????sampler2D?_MainTex;?? ?????? ????uniform?half4?_MainTex_TexelSize;?? ?????? ????uniform?half?_DownSampleValue;?? ?? ?????? ????struct?VertexInput?? ????{?? ?????????? ????????float4?vertex?:?POSITION;?? ?????????? ????????half2?texcoord?:?TEXCOORD0;?? ????};?? ?? ?????? ????struct?VertexOutput_DownSmpl?? ????{?? ?????????? ????????float4?pos?:?SV_POSITION;?? ?????????? ????????half2?uv20?:?TEXCOORD0;?? ?????????? ????????half2?uv21?:?TEXCOORD1;?? ?????????? ????????half2?uv22?:?TEXCOORD2;?? ?????????? ????????half2?uv23?:?TEXCOORD3;?? ????};?? ?? ?? ?????? ????static?const?half4?GaussWeight[7]?=?? ????{?? ????????half4(0.0205,0.0205,0.0205,0),?? ????????half4(0.0855,0.0855,0.0855,0),?? ????????half4(0.232,0.232,0.232,0),?? ????????half4(0.324,0.324,0.324,1),?? ????????half4(0.232,0.232,0.232,0),?? ????????half4(0.0855,0.0855,0.0855,0),?? ????????half4(0.0205,0.0205,0.0205,0)?? ????};?? ?? ?? ?????? ????VertexOutput_DownSmpl?vert_DownSmpl(VertexInput?v)?? ????{?? ?????????? ????????VertexOutput_DownSmpl?o;?? ?? ?????????? ?????????? ????????o.pos?=?mul(UNITY_MATRIX_MVP,?v.vertex);?? ?????????? ????????o.uv20?=?v.texcoord?+?_MainTex_TexelSize.xy*?half2(0.5h,?0.5h);;?? ????????o.uv21?=?v.texcoord?+?_MainTex_TexelSize.xy?*?half2(-0.5h,?-0.5h);?? ????????o.uv22?=?v.texcoord?+?_MainTex_TexelSize.xy?*?half2(0.5h,?-0.5h);?? ????????o.uv23?=?v.texcoord?+?_MainTex_TexelSize.xy?*?half2(-0.5h,?0.5h);?? ?? ?????????? ????????return?o;?? ????}?? ?? ?????? ????fixed4?frag_DownSmpl(VertexOutput_DownSmpl?i)?:?SV_Target?? ????{?? ?????????? ????????fixed4?color?=?(0,0,0,0);?? ?? ?????????? ????????color?+=?tex2D(_MainTex,?i.uv20);?? ????????color?+=?tex2D(_MainTex,?i.uv21);?? ????????color?+=?tex2D(_MainTex,?i.uv22);?? ????????color?+=?tex2D(_MainTex,?i.uv23);?? ?? ?????????? ????????return?color?/?4;?? ????}?? ?? ?????? ????struct?VertexOutput_Blur?? ????{?? ?????????? ????????float4?pos?:?SV_POSITION;?? ?????????? ????????half4?uv?:?TEXCOORD0;?? ?????????? ????????half2?offset?:?TEXCOORD1;?? ????};?? ?? ?????? ????VertexOutput_Blur?vert_BlurHorizontal(VertexInput?v)?? ????{?? ?????????? ????????VertexOutput_Blur?o;?? ?? ?????????? ?????????? ????????o.pos?=?mul(UNITY_MATRIX_MVP,?v.vertex);?? ?????????? ????????o.uv?=?half4(v.texcoord.xy,?1,?1);?? ?????????? ????????o.offset?=?_MainTex_TexelSize.xy?*?half2(1.0,?0.0)?*?_DownSampleValue;?? ?? ?????????? ????????return?o;?? ????}?? ?? ?????? ????VertexOutput_Blur?vert_BlurVertical(VertexInput?v)?? ????{?? ?????????? ????????VertexOutput_Blur?o;?? ?? ?????????? ?????????? ????????o.pos?=?mul(UNITY_MATRIX_MVP,?v.vertex);?? ?????????? ????????o.uv?=?half4(v.texcoord.xy,?1,?1);?? ?????????? ????????o.offset?=?_MainTex_TexelSize.xy?*?half2(0.0,?1.0)?*?_DownSampleValue;?? ?? ?????????? ????????return?o;?? ????}?? ?? ?????? ????half4?frag_Blur(VertexOutput_Blur?i)?:?SV_Target?? ????{?? ?????????? ????????half2?uv?=?i.uv.xy;?? ?? ?????????? ????????half2?OffsetWidth?=?i.offset;?? ?????????? ????????half2?uv_withOffset?=?uv?-?OffsetWidth?*?3.0;?? ?? ?????????? ????????half4?color?=?0;?? ????????for?(int?j?=?0;?j<?7;?j++)?? ????????{?? ?????????????? ????????????half4?texCol?=?tex2D(_MainTex,?uv_withOffset);?? ?????????????? ????????????color?+=?texCol?*?GaussWeight[j];?? ?????????????? ????????????uv_withOffset?+=?OffsetWidth;?? ????????}?? ?? ?????????? ????????return?color;?? ????}?? ?? ?????? ????ENDCG?? ?? ????FallBack?Off?? }??
2.2?C#代碼部分
C#腳本文件的代碼可以從我們之前的幾篇分析屏幕特效實現的文章中重用(如這篇實現屏幕油畫特效的文章:http://blog.csdn.net/poem_qianmo/article/details/49719247),只用稍微改一點細節即可。?
貼出詳細注釋的配合Shader實現此特效的C#腳本:
[cpp]?view plaincopy print?
using?UnityEngine;?? using?System.Collections;?? ?? ?? [ExecuteInEditMode]?? ?? [AddComponentMenu("Learning?Unity?Shader/Lecture?15/RapidBlurEffect")]?? public?class?RapidBlurEffect?:?MonoBehaviour?? {?? ?????? ????#region?Variables?? ?????? ?????? ????private?string?ShaderName?=?"Learning?Unity?Shader/Lecture?15/RapidBlurEffect";?? ?? ?????? ????public?Shader?CurShader;?? ????private?Material?CurMaterial;?? ?? ?????? ????public?static?int?ChangeValue;?? ????public?static?float?ChangeValue2;?? ????public?static?int?ChangeValue3;?? ?? ?????? ????[Range(0,?6),?Tooltip("[降采樣次數]向下采樣的次數。此值越大,則采樣間隔越大,需要處理的像素點越少,運行速度越快。")]?? ????public?int?DownSampleNum?=?2;?? ?????? ????[Range(0.0f,?20.0f),?Tooltip("[模糊擴散度]進行高斯模糊時,相鄰像素點的間隔。此值越大相鄰像素間隔越遠,圖像越模糊。但過大的值會導致失真。")]?? ????public?float?BlurSpreadSize?=?3.0f;?? ?????? ????[Range(0,?8),?Tooltip("[迭代次數]此值越大,則模糊操作的迭代次數越多,模糊效果越好,但消耗越大。")]?? ????public?int?BlurIterations?=?3;?? ?? ????#endregion?? ?? ?????? ????#region?MaterialGetAndSet?? ????Material?material?? ????{?? ????????get?? ????????{?? ????????????if?(CurMaterial?==?null)?? ????????????{?? ????????????????CurMaterial?=?new?Material(CurShader);?? ????????????????CurMaterial.hideFlags?=?HideFlags.HideAndDontSave;?? ????????????}?? ????????????return?CurMaterial;?? ????????}?? ????}?? ????#endregion?? ?? ????#region?Functions?? ?????? ?????? ?????? ????void?Start()?? ????{?? ?????????? ????????ChangeValue?=?DownSampleNum;?? ????????ChangeValue2?=?BlurSpreadSize;?? ????????ChangeValue3?=?BlurIterations;?? ?? ?????????? ????????CurShader?=?Shader.Find(ShaderName);?? ?? ?????????? ????????if?(!SystemInfo.supportsImageEffects)?? ????????{?? ????????????enabled?=?false;?? ????????????return;?? ????????}?? ????}?? ?? ?????? ?????? ?????? ????void?OnRenderImage(RenderTexture?sourceTexture,?RenderTexture?destTexture)?? ????{?? ?????????? ????????if?(CurShader?!=?null)?? ????????{?? ?????????????? ?????????????? ????????????float?widthMod?=?1.0f?/?(1.0f?*?(1?<<?DownSampleNum));?? ?????????????? ????????????material.SetFloat("_DownSampleValue",?BlurSpreadSize?*?widthMod);?? ?????????????? ????????????sourceTexture.filterMode?=?FilterMode.Bilinear;?? ?????????????? ????????????int?renderWidth?=?sourceTexture.width?>>?DownSampleNum;?? ????????????int?renderHeight?=?sourceTexture.height?>>?DownSampleNum;?? ?? ?????????????? ?????????????? ????????????RenderTexture?renderBuffer?=?RenderTexture.GetTemporary(renderWidth,?renderHeight,?0,?sourceTexture.format);?? ?????????????? ????????????renderBuffer.filterMode?=?FilterMode.Bilinear;?? ?????????????? ????????????Graphics.Blit(sourceTexture,?renderBuffer,?material,?0);?? ?? ?????????????? ????????????for?(int?i?=?0;?i?<?BlurIterations;?i++)?? ????????????{?? ?????????????????? ?????????????????? ????????????????float?iterationOffs?=?(i?*?1.0f);?? ?????????????????? ????????????????material.SetFloat("_DownSampleValue",?BlurSpreadSize?*?widthMod?+?iterationOffs);?? ?? ?????????????????? ?????????????????? ????????????????RenderTexture?tempBuffer?=?RenderTexture.GetTemporary(renderWidth,?renderHeight,?0,?sourceTexture.format);?? ?????????????????? ????????????????Graphics.Blit(renderBuffer,?tempBuffer,?material,?1);?? ?????????????????? ????????????????RenderTexture.ReleaseTemporary(renderBuffer);?? ?????????????????? ?????????????????renderBuffer?=?tempBuffer;?? ?? ?????????????????? ?????????????????? ????????????????tempBuffer?=?RenderTexture.GetTemporary(renderWidth,?renderHeight,?0,?sourceTexture.format);?? ?????????????????? ????????????????Graphics.Blit(renderBuffer,?tempBuffer,?CurMaterial,?2);?? ?? ?????????????????? ?????????????????? ????????????????RenderTexture.ReleaseTemporary(renderBuffer);?? ?????????????????? ????????????????renderBuffer?=?tempBuffer;?? ????????????}?? ?? ?????????????? ????????????Graphics.Blit(renderBuffer,?destTexture);?? ?????????????? ????????????RenderTexture.ReleaseTemporary(renderBuffer);?? ?? ????????}?? ?? ?????????? ????????else?? ????????{?? ?????????????? ????????????Graphics.Blit(sourceTexture,?destTexture);?? ????????}?? ????}?? ?? ?? ?????? ?????? ?????? ????void?OnValidate()?? ????{?? ?????????? ????????ChangeValue?=?DownSampleNum;?? ????????ChangeValue2?=?BlurSpreadSize;?? ????????ChangeValue3?=?BlurIterations;?? ????}?? ?? ?????? ?????? ?????? ????void?Update()?? ????{?? ?????????? ????????if?(Application.isPlaying)?? ????????{?? ?????????????? ????????????DownSampleNum?=?ChangeValue;?? ????????????BlurSpreadSize?=?ChangeValue2;?? ????????????BlurIterations?=?ChangeValue3;?? ????????}?? ?????????? #if?UNITY_EDITOR?? ????????if?(Application.isPlaying?!=?true)?? ????????{?? ????????????CurShader?=?Shader.Find(ShaderName);?? ????????}?? #endif?? ?? ????}?? ?? ?????? ?????? ?????? ????void?OnDisable()?? ????{?? ????????if?(CurMaterial)?? ????????{?? ?????????????? ????????????DestroyImmediate(CurMaterial);?? ????????}?? ?? ????}?? ?? ?#endregion?? ?? }??
將此C#代碼拖拽到場景的主攝像機之上, 且你的工程中也存在2.1節中貼出的Shader代碼,那么就可以在Game窗口中看到經過了屏幕模糊特效的處理后的鏡頭效果。
?
而Inspector中可得到如下所示的腳本選項。
?
其中,有3個選項可以調節,他們分別是:
- [Down Sample Num] – 降采樣的次數。此值越大,則采樣間隔越大,需要處理的像素點越少,運行速度越快。
- [Blur Speread Size] -模糊擴散度。進行高斯模糊時,相鄰像素點的間隔。此值越大相鄰像素間隔越遠,圖像越模糊。但過大的值會導致失真。
- [Blur?Iterations]?-迭代次數。此值越大,則模糊操作的迭代次數越多,模糊效果越好,但消耗越大。
調節這三個參數,便可以在場景中定制出自己需要的模糊特效。
?
2.3?推薦幾組參數設置
這邊推薦幾組效果出色較為出色的參數預設,方便有需要的朋友定制出適合自己的效果。
?
?
?
?
三、最終實現的效果圖示
3.1?Low Poly風格的效果測試
?
3.2?卡通風格效果測試
?
?
?
?
3.3?寫實風格的效果測試
?
?
?
?
?
附1、本文配套源碼下載鏈接
? 【Github】本文Shader源碼
?
附2、Reference
?
[1]?https://en.wikipedia.org/wiki/Gaussian_blur
[2]?http://www.cnblogs.com/foxianmo/p/4931507.html
[3]《Real-Time Rendering 3rd》,p467-p473.
總結
以上是生活随笔為你收集整理的12.屏幕高斯模糊的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。