高斯模糊 【Unity Shader入門精要12.4】
阿新 • • 發佈:2018-12-17
Blit(src,dest,mat,pass)函式的作用,按照Unity官方API的說法是將src這個RT用mat這個材質中的某個pass渲染,然後複製到dest中。如果要給渲染加一些後處理效果(SSAO,HDR,bloom之類的),幾乎可以肯定會用到這個函式。根據Unity自帶文件中的例子,在OnRenderImage中呼叫Blit,然後用指定的mat渲染出來。
OnRenderImage(src,dest)是Camera的一個回撥(message),他會在camera執行渲染時候被呼叫,官方給的大部分Image Effect的實現都是用了這個回撥。
(記得分配新的RT,用完後手動 Release 掉)
using System.Collections; using System.Collections.Generic; using UnityEngine; //12.4 高斯模糊 public class GaussianBlur : PostEffectsBase { //宣告該效果需要的shader,並據此建立相應的材質 public Shader gaussianBlurShader; private Material gaussianBlurMaterial = null; public Material material { get {//gaussianBlurShader是指定的shader,對應了本節所用的shader gaussianBlurMaterial = CheckShaderAndCreateMaterial(gaussianBlurShader, gaussianBlurMaterial); return gaussianBlurMaterial; } } //在指令碼中提供調整高斯模糊的引數 //【高斯模糊迭代次數】Bulr iterations - Larger number means more blur(越大越模糊) [Range(0, 4)] public int iterations = 3; //【模糊範圍】Blur spread for each iteration - larger value means more blur(越大越模糊) [Range(0.2f, 3.0f)] public float blurSpread = 0.6f; //【縮放係數】 [Range(1, 8)] public int downSpread = 2; /*blurSpread,downSpread都是出於效能的考慮,在高斯核維數不變的情況下,_BlurSize越大,模糊程度越高,但取樣數卻不受到影響。 但過大的_BlurSize值會造成虛影。而downSpread越大,需要處理的畫素數越少,同時也能進一步提高模糊程度,但過大的downSpread可能會使影象畫素化*/ //one:最簡單的OnRenderImage /* private void OnRenderImage(RenderTexture src, RenderTexture dest) { if (material !=null) { int rtW = src.width; int rtH = src.height; //利用RenderTexture.GetTemporary函式分配一塊與螢幕影象大小相同的緩衝區【buffer】 RenderTexture buffer = RenderTexture.GetTemporary(rtW, rtH, 0); //高斯迷糊需要呼叫兩個Pass,需要使用一塊中間快取來儲存第一個Pass執行完畢之後得到的模糊結果 //Render the vertical pass(渲染垂直通道) //使用shader中的第一個pass【0】,對【src】進行處理,並將結果儲存在【buffer】中 Graphics.Blit(src, buffer, material, 0); //Render the horizontal pass(渲染水平通道) //使用shader中的第二個pass【1】,對【buffer】進行處理,並將結果儲存在【dest】中 Graphics.Blit(buffer, dest, material, 1); //釋放快取【buffer】 RenderTexture.ReleaseTemporary(buffer); }else { Graphics.Blit(src, dest); } }*/ //two //利用縮放對影象進行取樣,從而減少需要處理的畫素個數,提高效能 /* private void OnRenderImage(RenderTexture src, RenderTexture dest) { if (material !=null ) { //與one不同的是在宣告緩衝區大小時,用了小雨螢幕解析度的尺寸 int rtW = src.width / downSpread; int rtH = src.height / downSpread; RenderTexture buffer = RenderTexture.GetTemporary(rtW, rtH, 0); //將該臨時渲染紋理的濾波模式設定為雙線性 buffer.filterMode = FilterMode.Bilinear; // Graphics.Blit(src, buffer, material, 0); // Graphics.Blit(buffer, dest, material, 1); RenderTexture.ReleaseTemporary(buffer); }else { Graphics.Blit(src, dest); } }*/ //three,考慮了高斯模糊的迭代次數 private void OnRenderImage(RenderTexture src, RenderTexture dest) { if (material !=null) { int rtW = src.width / downSpread; int rtH = src.height / downSpread; //定義第一個快取【buffer0】 RenderTexture buffer0 = RenderTexture.GetTemporary(rtW, rtH, 0); //將該臨時渲染紋理的濾波模式設定為雙線性 buffer0.filterMode = FilterMode.Bilinear; //吧【src】中的影象縮放後儲存到【buffer0】 Graphics.Blit(src, buffer0); for (int i = 0; i < iterations; i++) { material.SetFloat("_BlurSize", 1.0f + i * blurSpread); //定義第二個快取【buffer1】 RenderTexture buffer1 = RenderTexture.GetTemporary(rtW, rtH, 0); //使用shader中的第一個pass【0】,對【buffer0】進行處理,並將結果儲存在【buffer1】中 Graphics.Blit(buffer0, buffer1, material, 0); //釋放【buffer0】 RenderTexture.ReleaseTemporary(buffer0); //把【buffer1】儲存到【buffer0】中 buffer0 = buffer1; //重新分配【buffer1】 buffer1 = RenderTexture.GetTemporary(rtW, rtH, 0); //呼叫第二個pass【1】重複。。。對【buffer0】進行處理,並將結果儲存在【buffer1】中 Graphics.Blit(buffer0, buffer1, material, 1); //釋放【buffer0】 RenderTexture.ReleaseTemporary(buffer0); //把【buffer1】儲存到【buffer0】中 buffer0 = buffer1; //【buffer0】將儲存最終的影象 } //把結果顯示到螢幕上 Graphics.Blit(buffer0, dest); //釋放【buffer0】 RenderTexture.ReleaseTemporary(buffer0); } else { Graphics.Blit(src, dest); } } }
//12.4 高斯模糊 Shader "Unlit/Chapter12-GaussianBlur" { Properties { _MainTex ("Base(RGB)", 2D) = "white" {} _BlurSize("Blur Size",Float)=1.0 } SubShader { /* CGINCLUDE 。。。。。。。。。 ENDCG 包含的程式碼不需要包含在任何Pass語義塊中,使用的時候只需要在Pass中直接指定需要使用的頂點著色器和片元著色器函式名即可 CGINCLUDE類似C++標頭檔案的功能. 由於高斯模糊需要定義兩個Pass,但他們使用的片元著色器程式碼是完全相同的,使用CGINCIUDE可以避免編寫兩個完全一樣的frag. */ CGINCLUDE #include "UnityCG.cginc" //定義屬性對應變數 sampler2D _MainTex; //由於要用到相鄰畫素的紋理座標,使用unity提供的_MainTex_TexelSize變數,以計算相鄰畫素的紋理座標偏移量 half4 _MainTex_TexelSize; float _BlurSize; //分別定義兩個Pass將要使用的頂點著色器 struct v2f { float4 pos : SV_POSITION; half2 uv[5]:TEXCOORD0; }; //豎直方向頂點著色器程式碼 v2f vertBlurVertical(appdata_img v) { v2f o; o.pos = UnityObjectToClipPos(v.vertex); half2 uv = v.texcoord; //陣列的第一個座標儲存了當前的取樣,而剩餘的四個座標則是高斯模糊中對鄰域取樣使用的紋理座標,屬性_BlurSize控制取樣距離 o.uv[0] = uv; o.uv[1] = uv + float2(0.0, _MainTex_TexelSize.y * 1.0) * _BlurSize; o.uv[2] = uv - float2(0.0, _MainTex_TexelSize.y * 1.0) * _BlurSize; o.uv[3] = uv + float2(0.0, _MainTex_TexelSize.y * 2.0) * _BlurSize; o.uv[4] = uv - float2(0.0, _MainTex_TexelSize.y * 2.0) * _BlurSize; return o; } //水平方向頂點著色器程式碼 v2f vertBlurHorizontal(appdata_img v) { v2f o; o.pos = UnityObjectToClipPos(v.vertex); half2 uv = v.texcoord; o.uv[0] = uv; o.uv[1] = uv + float2(_MainTex_TexelSize.x * 1.0, 0.0) * _BlurSize; o.uv[2] = uv - float2(_MainTex_TexelSize.x * 1.0, 0.0) * _BlurSize; o.uv[3] = uv + float2(_MainTex_TexelSize.x * 2.0, 0.0) * _BlurSize; o.uv[4] = uv - float2(_MainTex_TexelSize.x * 2.0, 0.0) * _BlurSize; return o; } //定義李定義兩個Pass共用的片元著色器 fixed4 fragBlur(v2f i) : SV_Target{ //一個5*5的二維高斯核可以拆分成兩個大小為5的一維高斯核,並且由於對稱性,只需要記錄3個高斯權重,也就是程式碼的Weight變數 float weight[3] = { 0.4026, 0.2442, 0.0545 }; //將sum初始化為當前的畫素值乘以它的權重值 fixed3 sum = tex2D(_MainTex, i.uv[0]).rgb * weight[0]; //根據對稱性,我們進行兩次迭代,每次迭代包含兩次紋理取樣,並把畫素值和權重相乘後的結果疊加到sum中 for (int it = 1; it < 3; it++) { sum += tex2D(_MainTex, i.uv[it ]).rgb * weight[it]; sum += tex2D(_MainTex, i.uv[it +1]).rgb * weight[it]; /* //書上原來是這個,做了一點改動, sum += tex2D(_MainTex, i.uv[it*2-1]).rgb * weight[it]; sum += tex2D(_MainTex, i.uv[it*2]).rgb * weight[it]; 改動後的對應關係如下: |權重|weight[2] | weight[1] |weight[0] |weight[1] |weight[2] | uv | uv[3] | uv[1] | uv[0] | uv[2] | uv[4] | Y |uv+(0,2) | uv+(0,1) | uv+(0,0) |uv+(0,-1) |uv+(0,-2) | X |uv+(2,0) | uv+(1,0) | uv+(0,0) |uv+(-1,0) |uv+(-2,0) 2【uv 3】 1【uv 1】 -2【uv 4】 -1【uv 2】 0【uv 0】 1【uv 1】 2【uv 3】 -1【uv 2】 -2【uv 4】 */ } //最後函式返回濾波結果sum return fixed4(sum, 1.0); } ENDCG ZTest Always Cull Off Zwrite Off Pass { //方便以後呼叫 NAME"GAUSSIAN_BLUR_VERTICAL" CGPROGRAM #pragma vertex vertBlurVertical #pragma fragment fragBlur ENDCG } Pass { NAME"GAUSSIAN_BLUR_HORIZONTAL" CGPROGRAM #pragma vertex vertBlurHorizontal #pragma fragment fragBlur ENDCG } } FallBack "Diffuse" }