1. 程式人生 > >Unity屏幕後處理

Unity屏幕後處理

        後處理:渲染完整個場景得到螢幕影象後,對這個影象進行操作。

        過程:攝像中新增屏幕後處理指令碼,OnRenderImage(RenderTexture src, RenderTexture dest)會將當前渲染的影象儲存在src的紋理中,使用Graphics.Blit(RenderTexture src,  RenderTexture dest, Material material)和特定的Shader對當前影象進行處理,再把dest顯示在螢幕上。注:OnRenderImage是Mono的函式。Graphics.Blit會將第一個引數傳給Shader中_MainTex屬性。

        渲染狀態:關閉深度寫入。

        1、簡單的調整亮度、飽和度、對比度

        fixed4 frag(v2f i) : SV_Target

         {

                fixed4 renderTex = tex2D(_MainTex, i.uv);

                 fixed3 finalColor = renderTex.rgb * _Brightness;

                 fixed luminance = 0.2125 * renderTex.r + 0.7154 * renderTex.g + 0.0721 * renderTex.b;

                 fixed3 luminanceColor = fixed3(luminance, luminance, luminance);

                  finalColor = lerp(luminanceColor, finalColor, _Saturation);

                 fixed3 avgColor = fixed3(0.5, 0.5, 0.5);

                  finalColor = lerp(avgColor, finalColor, _Contrast);

                 return fixed4(finalColor, renderTex.a);

         }

         2、邊緣檢測

         卷積操作:使用卷積核對影象的每個畫素進行操作。常見的邊緣卷積運算元(卷積核)有Sobel,Roberts等。

         由於卷積需要對相鄰區域內的紋理進行取樣,需要利用_MainTex_TexelSize(可以訪問紋理對應的每個紋素的大小)計算相鄰區域的紋理座標。下面是使用Sobel運算元實現邊緣檢測。

v2f vert(appdata_img v) {
  v2f o;
  o.pos = UnityObjectToClipPos(v.vertex); 
  half2 uv = v.texcoord;   
   o.uv[0] = uv + _MainTex_TexelSize.xy * half2(-1, -1);
   o.uv[1] = uv + _MainTex_TexelSize.xy * half2(0, -1);
   o.uv[2] = uv + _MainTex_TexelSize.xy * half2(1, -1);  
   o.uv[3] = uv + _MainTex_TexelSize.xy * half2(-1, 0);
   o.uv[4] = uv + _MainTex_TexelSize.xy * half2(0, 0);
   o.uv[5] = uv + _MainTex_TexelSize.xy * half2(1, 0);
   o.uv[6] = uv + _MainTex_TexelSize.xy * half2(-1, 1);
   o.uv[7] = uv + _MainTex_TexelSize.xy * half2(0, 1);
   o.uv[8] = uv + _MainTex_TexelSize.xy * half2(1, 1); 
   return o;//儲存了鄰域的紋理座標,從片元放在頂點著色器,減少運算,提高效能。
 }

片元著色器:

fixed4 fragSobel(v2f i) : SV_Target {
   half edge = Sobel(i);  //計算當前畫素的梯度值
   fixed4 withEdgeColor = lerp(_EdgeColor, tex2D(_MainTex, i.uv[4]), edge);
   fixed4 onlyEdgeColor = lerp(_EdgeColor, _BackgroundColor, edge);
   return lerp(withEdgeColor, onlyEdgeColor, _EdgeOnly);
 } 

簡單解釋一下lerp:

float lerp(float a, float b, float w) {
  return a + w*(b-a);
}

Sobel函式如下:

fixed luminance(fixed4 color) {
    return  0.2125 * color.r + 0.7154 * color.g + 0.0721 * color.b;
 }
 

half Sobel(v2f i) {
   const half Gx[9] = {-1,  0,  1,
                                  -2,  0,  2,
                                 -1,  0,  1};
  const half Gy[9] = {-1, -2, -1,

                                 0,  0,  0,
                                1,  2,  1};  

    half texColor;
    half edgeX = 0;
    half edgeY = 0;
    for (int it = 0; it < 9; it++) {
         texColor = luminance(tex2D(_MainTex, i.uv[it]));
         edgeX += texColor * Gx[it]; 
         edgeY += texColor * Gy[it]; 
      }
    half edge = 1 - abs(edgeX) - abs(edgeY);
    return edge;
 }

        對9個畫素進行取樣,計算亮度值,再與水平和豎直方向的卷積核中對應的權重相乘,最後疊加到各自的梯度值上。edge越小越是邊緣。

        注:上述我們是對螢幕的顏色資訊進行的邊緣檢測,由於實際中紋理、陰影等很多資訊會影響這個結果,因此,會對深度紋理和法線紋理進行邊緣檢測。

        3、高斯模糊

         均值模糊也是使用了卷積操作,且卷積核中的每個元素值相等,且相加為1,卷積後得到的畫素值是其相鄰域中各個畫素的平均值。中值模糊是鄰域中所有畫素排序後取中值為畫素顏色。

        高斯模糊使用的卷積核叫高斯核。高斯核是正方形的,每個元素按下面這個方程計算,x,y對應當前到中心的距離。

        高斯核的維數越高,模糊程度越大。N*N的高斯核對W*H的影象要取樣N*N*W*H次。但是,我們可以將二維的拆成兩個一維的,使用兩個一維的先後濾波,取樣次數2*N*W*H。

         4、bloom效果

         讓畫面中較亮的部分擴散到周圍,朦朧感。實現方法:根據一個閾值將影象中較亮的部分存在一張紋理中,再對這張紋理高斯模糊,最後將它和原影象混合。

        提取較亮的部分:

        fixed4 c = tex2D(_MainTex, i.uv);

        fixed val = clamp(luminance(c) - _Luminance, 0.0, 1.0);

         return c * val;

        5、SSAO(螢幕空間環境光遮蔽)

        用來模擬環境光,被遮蔽的物體環境光要稍微暗一些。

        原理:對鋪屏四邊形上的每片段,根據周邊深度值計算遮蔽因子。這個遮蔽因子用來減少或者抵消片段的環境光照分量。高於片段深度值樣本的個數就是遮蔽因子。遮蔽因子是通過採集片段周圍球型核心(Kernel)的多個深度樣本,並和當前片段深度值對比而得到的。

       實現方式(有很多種):

       基於法線的半球積分:計算P的遮擋值,需要把它周圍的畫素都當成一個小球,然後計算這個小球對它遮擋值的總和。影響因素有:周圍畫素點到P的距離,周圍畫素點到P的向量V和P點的法線的夾角。

       基於視線方向的球積分。

       據不同的需求給SSAO取樣點分級,比如要好點的效果可以取樣32次以上,要效能高點可以取樣8次就夠了。有較高要求的SSAO給結果進行模糊處理 降噪 會使明暗程度更加平滑。在較低次數取樣下基於View方向的球積分效果表現更好,在較高次數取樣下基於normal方向的半球積分表現效果更好。總的來說基於normal方向的計算效果更佳逼真。

        6、DOF(景深)

       現實世界中,相機只能聚焦到一個特定距離的焦平面,焦平面內側和外側較遠的物體都會被虛化模糊。景深效果可以提高場景深度感,突出重點物體,比如人物攝影的時候虛化背景。焦點上的清晰度是最高的,其餘的影像清晰度隨著它與焦點的距離成正比例下降。

        原理:通過兩張圖片,一張清晰的,一張經過高斯模糊的,然後根據圖片中每個畫素的深度值在兩張圖片之間差值,就可以達到景深的效果了。模糊的方法可以用前面介紹的高斯模糊。

        Camera Depth Texture記錄了螢幕空間所有物體距離相機的距離。

        參考:https://blog.csdn.net/puppet_master/article/details/52819874,寫的很不錯,實現也有。

       7、SSR

       8、AA

會增加記憶體消耗,低記憶體機型上關掉。

     (持續更新中)