unity | 后处理篇

Posted A札

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了unity | 后处理篇相关的知识,希望对你有一定的参考价值。

unity|后处理篇


前言

在unity中实现后处理效果大致有两种方式,一种是通过插件的方式,常用的方法就是使用unity内置的插件Post-Processing。第二种方式就是使用脚本获取到渲染后帧缓冲区的图像,再通过shader写后处理的效果,最后合并输出图像到屏幕上。

这里记录的后处理全部基于unity内置渲染管线展开,URP渲染管线和内置渲染管线的后处理方式会有些许差异。post-processing在内置渲染管线和URP渲染管线中也是会有一些差异的,具体可以去看官方文档。
第二种方式是使用unity内置的方法OnRenderImage()这个方法实现,但是这个方法是在unity内置渲染管线中的,在URP渲染管线中并不会生效,当然在URP中也有替代方法去实现,这里不考虑这个。


一、Post-Processing

更详细具体可以看unity中Post-Processing官方文档

1、 Post-Processing的使用

  • ①、安装插件。
    window->Packages Manager->Post-Processing->install。
  • ②、创建文件和添加组件。
    创建Post-Processing Profile文件;新建空游戏物体,并且添加Post Process Volume组件;在相机上添加Post Process Layer组件。
  • ③、设置层级Layer。
    添加了Post Process Volume组件游戏物体、添加Post Process Layer组件的相机的层级都设置为同一个层级,如设置层级为post-Processing。
    并且在Layer组件中设置的Layer也要一致。
  • ④、挂载文件。
    在Post Process Volume组件中挂载一开始创建的post-processing profile文件。
    如果需要效果应用全局需要把Is Global勾选上。
  • ⑤、添加后处理效果。
    在Post Process Volume组件中,点击Add Effect -> Unity -> (后处理效果)。

2、Post-Processing后处理效果

抗锯齿

锯齿是图形出现“楼梯”状的锯齿,抗锯齿效果使图形边缘更加平滑,一般图片在像素不够高或者图片被放大的情况下就会出现这种锯齿情况。

unity默认的抗锯齿设置在Edit -> Project Settings ->Quality中设置。
而Post-Processing的抗锯齿,是在相机中的Post Process Layer组件Anti - Aliasing。(抗锯齿的效果不是在Post Process Volume组件中通过Add Effect添加的,其他的效果是在Post Process Volume组件中通过Add Effect添加的)

No - Anti - aliasling:无抗锯齿效果。

①、Ambient Occlusion 环境光遮蔽

环境光遮蔽比较消耗性能,因此不建议在手游中使用。
一些凹陷的地方或者被周围遮挡的地方,往往会被遮挡住周围的光线,因此会显得更暗。

  • Mode | 模式 (Scalable Ambient Obscurance可扩展的环境观测、Multi Scale Volumetric Obscurance多规模的体积测量)
  • Intensity | 强度
  • Radius | 半径/幅度
  • Quality | 质量
  • Color | 颜色
  • Ambient Only | 仅周围环境

②、Auto Exposure 自动曝光

自动曝光效果根据图像包含的亮度级别范围来动态调整图像的曝光。

  • Filtering (%) |
  • Minimum(EV) | 最低限制
  • Maximum(EV) | 最高限制
  • Exposure Compensation | 曝光补偿

Adaptation 适应性

  • Type | 类型
  • Speed Up | 加速
  • Speed Down | 减速

③、Bloom 辉光/泛光

在unity中的灯光,或者材质球中有发光材质,输出显示是没有辉光的,为了营造更真实的光感,可以在后处理的中加入辉光的效果。

  • Intensity | 强度
  • Threshold | 阈值
  • Soft Knee | 软膝关节
  • Clamp | 钳制
  • Diffusion | 扩散
  • Anamorphic Ratio | 拟态比
  • Color | 颜色
  • Fast Mode | 快速模式(开启快速模式会省一点性能,适合手游模式)
  • (Dirtiness) Texture| 纹理
  • (Dirtiness) Intensity | 脏迹强度

④、Chromatic Aberration | 色差

色差效果可以模仿真实摄像机在镜头无法将所有颜色融合到同一点时产生的效果。
也可以粗略做镜头的运动模糊效果,有一种运动时的速度感。即图像四周会有色值偏差。

  • Spectral Lut |
  • Instensity | 强度
  • Fast Mode | 快速模式

⑤、Color Grading 色调/颜色分级

颜色分级效果可以改变或校正 Unity 产生的最终图像的颜色和亮度。类似于添加滤镜。

  • Mode |模式
  • Lookup Texture | 查找纹理
  • Contribution

White Balance白平衡

  • Temperature | 色温
  • Tint | 色调

Tone

  • Color Filter | 颜色滤镜
  • Hue Shift | 色相转换
  • Saturation | 饱和度
  • Brightness | 明亮度
  • Contrast | 对比度

Channel Mixer通道混合

  • Red
  • Green
  • Blue
  • Trackballs |
    Grading Curve 分级曲线

⑥、Depth Of Field 景深

  • Focus Distance | 焦距
  • Apperture | 适应性
  • Focal Length | 镜头长度
  • Max Blur Size | 最大的模糊大小

⑦、Grain

类似于添加噪波噪点

  • Colored | 噪波噪点是否为彩色
    开启的噪波噪点是带有rgb色彩的,不开启的噪波噪点是黑白色的。
  • Intensity | 强度
    从0到1强度大小,整体噪波噪点的强度
  • Size | 大小
    噪波噪点的大小
  • Luminance Contribution

⑧、Lens Distortion 镜头变形失真

默认情况下,只调整强度大小的效果类似于凹凸镜的效果。

  • Intensity | 强度:调整的是变形的强度大小
  • X Multiplier | X轴的程度:X轴上镜头变形的程度
  • Y Multiplier | Y轴的程度:Y轴上镜头变形的程度
  • Center X | X轴的中心:镜头默认是从中间的点来进行变形,可以通过调整这个数值调整X轴上的中心点进行偏移
  • Center Y | Y轴的中心:对Y轴上的中心点进行偏移
  • Scale | 大小:正常画面大小为1,大于1则画面被整体放大;小于1,整个画面整体被缩小,但是整体画面被缩小,显示出来的就会填不满画布,填不满的部分就会被图像的边缘填充。

⑨、Motion Blur 运动模糊

  • Shutter Angle | 快门角度
  • Samole Count | 抽样计数

⑩、Screen Space Reflections 屏幕空间反射

  • Preset | 预设
  • Maximum March Distance | 最大行进距离
  • Distance Fade | 距离衰减
  • Vignette | 渐变

11、Vignette 渐晕

渐晕效果使图像的边缘变暗,让图像的中心更亮。

  • Mode | 模式(Classic、 Masked)
  • Color | 颜色
  • Center | 中心
  • Intensity | 强度
  • Smoothness | 平滑度
  • Roundness
  • Rounded | 圆角

二、内置管线中OnRenderImage()方法实现

大致实现方法:
如果是在unity内置管线中,则相机挂载使用C#脚本用内置函数OnRenderImage来调用渲染管线中帧缓冲区的图像,再调用shader,对图像进行二次修改,实现后处理的效果。(新建一个材质球创建挂载shader,对shader内容进行编写后处理效果的编写。)

unity商店里面有一些免费资源下载下来也会有这些后处理和shader脚本,可以自行下载来研究,涉及到的太广泛,就不写了。


Unity Shader 屏幕后效果——Bloom外发光

Bloom的原理很简单,主要是提取渲染图像中的亮部区域,并对亮部区域进行模糊处理,再与原始图像混合而成。

 

一般对亮部进行模糊处理的部分采用高斯模糊,关于高斯模糊,详见之前的另一篇博客:

https://www.cnblogs.com/koshio0219/p/11152534.html

 

计算方法:

总共需要用到4个Pass,它们的顺序如下:

Pass 1:得到纹理的亮度值(灰度值),由此计算出亮部区域,传递给一个临时的新纹理,这里叫_Bloom

Pass 2,3:单独对_Bloom进行高斯模糊(纵横),_Bloom纹理更新

Pass 4:混合原始纹理和_Bloom纹理,得到最终效果

 

为了得到更为细致的Bloom效果,建议将游戏的颜色空间由默认的伽马空间转为线性空间,必要时还可开启HDR

 

控制脚本:

 1 using UnityEngine;
 2 
 3 public class BloomCtrl : ScreenEffectBase
 4 
 5     private const string _LuminanceThreshold = "_LuminanceThreshold";
 6     private const string _BlurSize = "_BlurSize";
 7     private const string _Bloom = "_Bloom";
 8 
 9     [Range(0, 4)]
10     public int iterations = 3;
11     [Range(0.2f, 3.0f)]
12     public float blurSize = 0.6f;
13     [Range(1, 8)]
14     public int dowmSample = 2;
15     [Range(0.0f, 4.0f)]
16     public float luminanceThreshold = 0.6f;//控制Bloom效果的亮度阈值,因为亮度值大多数时不大于1,故该值超过1时一般无效果,但开启HDR后图像的亮度取值范围将扩大
17 
18     private void OnRenderImage(RenderTexture source, RenderTexture destination)
19     
20         if (Material != null)
21         
22             Material.SetFloat(_LuminanceThreshold, luminanceThreshold);
23 
24             int rth = source.height / dowmSample;
25             int rtw = source.width / dowmSample;
26 
27             RenderTexture buffer0 = RenderTexture.GetTemporary(rtw, rth, 0);
28             buffer0.filterMode = FilterMode.Bilinear;
29 
30             //第1个Pass中提取纹理亮部,存到buffer0中,以便后面进行高斯模糊处理
31             Graphics.Blit(source, buffer0,Material,0);
32 
33             for(int i = 0; i < iterations; i++)
34             
35                 Material.SetFloat(_BlurSize, blurSize*i+1.0f);
36 
37                 //第2,3个Pass中对亮部分别进行纵向和横向的渲染处理(高斯模糊)
38                 RenderTexture buffer1 = RenderTexture.GetTemporary(rtw, rth, 0);
39                 Graphics.Blit(buffer0, buffer1, Material,1);
40                 RenderTexture.ReleaseTemporary(buffer0);//临时创建的渲染纹理不能直接释放 x: buffer0.Release();
41 
42                 buffer0 = RenderTexture.GetTemporary(rtw, rth, 0);
43                 Graphics.Blit(buffer1, buffer0, Material, 2);
44                 RenderTexture.ReleaseTemporary(buffer1);
45             
46 
47             //第4个Pass将buffer0高斯模糊后的结果传给_Bloom以进行最后的混合
48             Material.SetTexture(_Bloom, buffer0);
49             Graphics.Blit(source,destination,Material,3);//注意这里用原始纹理作为源纹理而不是buffer0,因为buffer0已经作为另一个参数进行了传递,而这里还需要原始的纹理以进行混合
50             RenderTexture.ReleaseTemporary(buffer0);
51         
52         else
53             Graphics.Blit(source, destination);
54     
55 

 

Shader脚本:

  1 Shader "MyUnlit/Bloom"
  2 
  3     Properties
  4     
  5         _MainTex ("Texture", 2D) = "white" 
  6         _Bloom("Bloom",2D)="black"
  7         _LuminanceThreshold("Luminance Threshold",Float)=0.5
  8         _BlurSize("Blur Size",Float)=1.0
  9     
 10     SubShader
 11     
 12         CGINCLUDE
 13 
 14         #include "UnityCG.cginc"
 15 
 16         sampler2D _MainTex;
 17         half4 _MainTex_TexelSize;
 18         sampler2D _Bloom;
 19         float _LuminanceThreshold;
 20         float _BlurSize;
 21 
 22         struct v2f
 23         
 24            half2 uv : TEXCOORD0;
 25            float4 pos : SV_POSITION;
 26         ;
 27 
 28         struct v2fBloom
 29         
 30            //half4是因为这里还要存储_Bloom纹理
 31            half4 uv:TEXCOORD0;
 32            float4 pos:SV_POSITION;
 33         ;
 34 
 35         v2f vert(appdata_img v)
 36         
 37            v2f o;
 38            o.pos=UnityObjectToClipPos(v.vertex);
 39            o.uv=v.texcoord;    
 40            return o;
 41         
 42 
 43         v2fBloom vertBloom(appdata_img v)
 44         
 45            v2fBloom o;
 46            o.pos=UnityObjectToClipPos(v.vertex);
 47 
 48            //xy存储主纹理,zw存储_Bloom纹理,这样不必再申请额外空间
 49            o.uv.xy=v.texcoord;
 50            o.uv.zw=v.texcoord;
 51 
 52            //纹理坐标平台差异化判断,主要针对DirectX,因为DirectX与OpenGL纹理坐标原点不同(分别在左上和左下)
 53            //同时Unity平台对于主纹理已经进行过内部处理,因此这里只需要对_Bloom纹理进行平台检测和翻转
 54            //主要表现为进行y轴方向的翻转(因为y轴方向相反),对于_Bloom纹理来说也就是w
 55            #if UNITY_UV_STARTS_AT_TOP
 56            if(_MainTex_TexelSize.y<0)
 57                   o.uv.w=1.0-o.uv.w;
 58            
 59            #endif
 60 
 61            return o;
 62         
 63 
 64         //提取超过亮度阈值的图像
 65         fixed4 fragExtractBright(v2f i):SV_Target
 66         
 67             fixed4 col=tex2D(_MainTex,i.uv);
 68             fixed val=clamp(Luminance(col)-_LuminanceThreshold,0.0,1.0);
 69             return col*val;
 70         
 71 
 72         //对xy和zw对应的纹理采样进行混合
 73         fixed4 fragBloom(v2fBloom i):SV_Target
 74         
 75             return tex2D(_MainTex,i.uv.xy)+tex2D(_Bloom,i.uv.zw);
 76         
 77 
 78         ENDCG
 79 
 80         ZTest Always
 81         Cull Off
 82         ZWrite Off
 83 
 84         //Pass 1:提亮部
 85         Pass
 86         
 87             CGPROGRAM
 88             #pragma vertex vert
 89             #pragma fragment fragExtractBright     
 90             ENDCG
 91         
 92 
 93         //Pass 2,3:高斯模糊,这里直接调用以前写的Pass
 94         UsePass "MyUnlit/GaussianBlur/GAUSSIANBLUR_V"
 95 
 96         UsePass "MyUnlit/GaussianBlur/GAUSSIANBLUR_H"
 97 
 98         //Pass 4:混合原图和模糊后亮部
 99         Pass
100         
101             CGPROGRAM
102             #pragma vertex vertBloom
103             #pragma fragment fragBloom
104             ENDCG
105         
106     
107     Fallback Off
108 

 

效果如下:

技术图片技术图片

 

以上是关于unity | 后处理篇的主要内容,如果未能解决你的问题,请参考以下文章

iOS Unity3D游戏引擎入门②

Unity Shader入门精要学习笔记 - 第12章 屏幕后处理效果

使用Photon引擎进行unity网络游戏开发

Unity 2D精灵(Sprite)与2D物理组件

AssetBundle理论篇

如何在 Unity 游戏引擎平台上的单元测试中实例化 MonoBehaviour 对象