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

会增加内存消耗,低内存机型上关掉。

     (持续更新中)


 

猜你喜欢

转载自blog.csdn.net/jfy307596479/article/details/85786260