Unity Shader 後處理 簡單均值模糊

2021-07-29 08:52:28 字數 4251 閱讀 8437

今天來學習一下後處理中比較常用的一種效果,螢幕模糊效果。模糊效果,在影象處理中經常用到,photoshop中也有類似的濾鏡。我們在遊戲中也會經常用到。因為螢幕模糊效果是一些高階後處理效果的基礎,比如景深等效果都需要用螢幕模糊效果來實現,所以我們首先看一下螢幕模糊效果,然後通過螢幕模糊,進一步學習景深效果與運動模糊效果的實現。

所謂模糊,也就是不清楚,清晰的,各個畫素之間會有明顯的過渡,而如果各個畫素之間的差距不是很大,那麼影象就會模糊了,極端一點的情況,當一張所有的畫素之間顏色都差不多時,那麼這張也就是乙個純色的了。模糊操作就是讓畫素間的顏色差距變小,比如a點是紅色,a點周圍的點是綠色,模糊就像用一把刷子,將a點和周圍的點的顏色混合起來,變成最終的顏色。而怎樣混合,按照不同的權值進行混合,就可以達到不同的效果了。比如均值模糊,以及著名的高斯模糊。

影響模糊程度的重要因素是模糊半徑,

模糊半徑越大,模糊程度越大,模糊半徑越小,模糊程度越小。那麼,模糊半徑是什麼?所謂模糊半徑,也就是我們取樣的乙個範圍,比如我們模糊的半徑很小,只是把畫素和它周圍的一圈定點混合,那麼模糊的程度就很小,而如果我們加大模糊半徑,極端情況是每個頂點都取了周圍所有點,也就是整張圖的畫素平均值,那麼這張圖的顏色就會偏向一種顏色。

最簡單的,我們看一下簡單的模糊,直接用周圍畫素求和平均,我們混合的最終影象,在某一點的權重僅僅跟模糊半徑有關。換句話說,比如模糊半徑為1,那麼,我們取乙個畫素點,以及他周圍的一圈畫素點,一共九個點,直接取平均,那麼每個點的權重設定為1/9。這也就是所謂的均值模糊。我們看一下均值模糊的例子。

shader部分:

shader "custom/******blureffect"   

} //通過cginclude我們可以預定義一些下面在pass中用到的struct以及函式,

//這樣在pass中只需要設定渲染狀態以及呼叫函式,shader更加簡潔明瞭

cginclude

//cg檔案,包含了unity內建的一些cg函式

#include "unitycg.cginc"

//blur結構體,從blur的vert函式傳遞到frag函式的引數

struct v2f_blur

; //用到的變數

sampler2d _maintex;

//xx_texelsize,xx紋理的畫素相關大小width,height對應紋理的解析度,x = 1/width, y = 1/height, z = width, w = height

float4 _maintex_texelsize;

//模糊半徑

float _blurradius;

//vertex shader

//fragment shader

fixed4 frag_blur(v2f_blur i) : sv_target

endcg

//子著色器

subshader

//直接呼叫vert_blur和frag_blur

cgprogram

#pragma vertex vert_blur

#pragma fragment frag_blur

endcg

} }

}

c#指令碼部分:

using unityengine;  

using system.collections;

//編輯狀態下也執行

[executeineditmode]

//繼承自posteffectbase

public class ******blureffect : posteffectbase

} }

unity shader-後處理:簡單的顏色調整(亮度,飽和度,對比度)中有該類的完整實現,此處不予貼出**。

效果如下圖所示:

原圖效果

從上面的模糊效果我們看到,模糊半徑越大,模糊的效果越明顯。但是!這種效果看起來一點都不舒服,有種近視的趕腳,完全不是平滑的模糊效果,就更不要說進一步的毛玻璃之類的全模糊效果了。

既然,一次模糊我們感覺效果不是很盡人意,那麼,我們可以嘗試迭代模糊,也就是用上一次模糊的輸出作為下一次模糊的輸入,迭代之後的模糊效果更加明顯。先看一下**,這次,我們的shader**和上面的一樣,沒有變動,僅僅是修改了指令碼,增加了降解析度和迭代的兩個操作。

using unityengine;  

using system.collections;

//編輯狀態下也執行

[executeineditmode]

//繼承自posteffectbase

public class ******blureffect : posteffectbase

//將結果拷貝到目標rt

graphics.blit(rt1, destination);

//釋放申請的兩塊renderbuffer內容

rendertexture.releasetemporary(rt1);

rendertexture.releasetemporary(rt2);

} }

}

結果如下:

我們看到,通過迭代以及降低解析度,我們的模糊效果更加明顯了,當迭代次數較大時,會有一種毛玻璃的效果。這裡,雖然迭代次數增加了,會耗費更多的效能,但是相應地,我們也降低了解析度,也減少了取樣等計算操作的消耗。

這裡,我們通過多次處理,包括降解析度以及迭代,完成了模糊操作,這裡我們需要臨時儲存上一次處理過的中間輸出,所以就需要用渲染中常用的乙個概念rendertexture。

關於rendertexture,簡要介紹一下,我們在渲染場景時,一般都是直接輸出到幀快取,然後輸出到螢幕上,然而有的時候,我們並不想直接輸出結果,而是需要對這個渲染的結果進行處理,所以,我們就將渲染的結果輸出到了一張紋理上,也就是rendertexture,這也是所有後處理的基礎。unity的rendertexture還是很好用的,我們不僅僅可以在後處理時使用,還可以通過把攝像機的輸出設定到某個rt上,然後用這張rt作為一些類似鏡子的物體上,就可以實現鏡面效果或者螢幕效果。

不過 rendertexture還是很耗費資源的,一張大的rendertexture是螢幕解析度大小的一張,而且是完全不能夠壓縮的,所以當後處理中rendertexture用得多時,記憶體消耗很大,在手機,尤其是大屏手機,記憶體比較小的情況下,

後處理疊加時很可能會由於記憶體耗盡而崩潰。所以,我們在使用rendertexture時需要慎重考慮。如果效果可以接受,我們就可以考慮降低rendertexture的解析度,這樣,輸出的畫面效果可能會打一些折扣,但是效能會有很大的提高。而我們的模糊效果,本身降低解析度就會導致畫面比較模糊,所以在這裡,我們完全可以放心大膽地降低rt的解析度,既可以提公升效果,又可以大大地減少開銷。

這裡還有一點,由於onrenderimage函式每幀在渲染之前都會呼叫,之前曾經擔心會不會每一幀在這裡申請rt,然後釋放,會不會有很高的gc?經過查詢了一些資料,發現unity這裡是進行過處理的,rendertexture是之前申請好的一塊記憶體區域,我們可以直接使用,而不需要考慮gc的問題,正如這兩個函式的名字一樣,rendertexture.gettemporary和rendertexture.releasetemporary一樣。並且,本人親測,使用profile掛了一下這個指令碼,發現的確沒有gc:

螢幕後處理

void onrenderimage rendertexture src,rendertexture dest 螢幕後處理函式 graphics.blit src,dest,mat,pass 螢幕後處理 src當前螢幕紋理 graphics.drawmesh amesh,vector3.zero,q...

DEM軌跡後處理

2020.6.16更新 首先在輸出顆粒資訊的時候儲存global id 然後在par iew中匯入vtp資料 不要匯入pvd 並使用temporal particle to pathlines這個filter 可以直接ctrl space調出搜尋框搜尋 首先用problocation功能顯示顆粒資料...

純後處理的volumetric light

按照先前的計畫,klayge 4.2中將加入volumetric light的效果,目前已經由parsifal wang實現並整合入引擎中。這裡的volumetric light,或者說light shaft,方法上類似於ce和3dmark的做法 把場景渲染得到的depth texture和colo...