屏幕后处理全家桶

什么是屏幕后处理全家桶

屏幕后期处理一般涉及到屏幕上许多像素的处理,消耗带宽较大。而多种屏幕后期叠加,会多次进行屏幕重绘、取帧缓存等操作,更加浪费。而且,多种屏幕后期叠加时需要正确的执行顺序,有些必须放到另一些的前面,否则会出现问题。所以我们用屏幕后处理全家桶,多种屏幕后期最终一起全屏渲染,减少浪费,而且全家桶用堆栈方式确定了多种屏幕后期处理顺序,避免错误。

当前版本和来源

我们的屏幕后期处理全家桶版本1.0.0是在unity的全家桶基础上修改得来的,针对unity的全家桶进行了以下修改:
1. 缩减不兼容的效果和通用shader变种;
2. 对使用的效果计算流程和计算方式进行简化,减少消耗;
3. 将项目组自定制的后期效果整合进屏幕后处理全家桶,减少pass,达到优化效果;

优势和问题

  1. 将多个后期效果最后综合在一起全屏渲染,减少重复操作、减少屏幕重绘,减少消耗;
  2. 集成了常用的后期效果,使用方便,并且保证它们顺序正确;
  3. 使用profile进行后期设置,方便不同项目组使用,定制不同的屏幕后处理;
  4. 高度集成化的脚本和shader使修订起来比较困难

消耗评估

  1. 常见移动平台使用的后期及一般消耗评估
    屏幕_后期消耗
  2. 骁龙820,同时开启bloom,outline和under water的一般耗时评估:
    bloom_outline_under_water消耗

注意事项

  1. 能接受的情况下尽量用小分辨率,能大幅度减少消耗;
  2. 尽量减少同时开启多个后期的时间;
  3. 对于消耗特别大的后期,对于影响性能的参数一定要谨慎设置;
  4. 要彻底关闭后期时,需要关闭外面post-process behavior脚本,否则不管用;
  5. 注意打包后期效果shader,以及相关的摄像机设置,比如depth等;
  6. 建议的分级策略,最高级——后期叠加,高级——单后期,中低级——无后期;
  7. 同级Soc机型策略:iPhone>snapdragon>others(kirin、MTK等)

环境配置

  1. 将MobilePostProcessing文件夹放到工程的Assets目录下;
  2. 将Post-Processing Behaviour脚本拖到摄像机上,如果有多个摄像机,每个摄像机上都要拖;
  3. 在脚本下面会出现Profile属性,点击右边的圆圈,在Assets签页下会出现post-processing profile对象,选择需要的;
  4. 点击上面选择的post-processing profile对象,在Inspector窗口就会出现屏幕后处理全家桶的编辑界面。
  5. 如果用到外发光效果,将需要外发光的物体拖到脚本上面的OutRen上,可以设置3个(建议程序在脚本里设置,也可以将inspector设为Debug模式,通过拖动设置,但不太建议,会生成变体)。
  6. 如果用到大气散射效果,将代表太阳的物体设置到脚本上面的Sun Shaft Tran上(同5,建议程序中设置)。
  7. 注意:Edit->Project Setting->Quality->Rendering下面,将Anti Aliasing设为Disabled

参数设置

在上面所述的编辑界面中,可以通过设置参数选择使用哪些屏幕特效以及设置每种特效的效果。
1. Debug Views:Mode保持默认值,设置为None;
2. Depth Of Field:景深
* Focus Distance:汇聚点到屏幕的距离(一般距离汇聚点越近的景物越清晰);
* Aperture:相机光圈,光圈越小越模糊;
* Use Camera FOV:从相机上设置的fov值自动计算焦距,建议不要勾选,因为对美术来说不太直观,不易理解
* Focal Length:焦距,焦距越大越模糊
* Kernel Size:卷积核大小,决定采样点的多少,越大越费,建议选Small(DOF中只有这个选项影响性能)
3. Bloom:泛光
* Intensity:bloom后明亮的强度,值越大越亮
* Threshold(Gamma):用于提取较亮区域使用的阈值
* Soft Knee:平滑度
* Radius:影响模糊迭代次数和采样半径,次数越大性能越差,建议小于4
* Anti Flicker:勾选上,精度会高,会比较费
4. User Lut:调主色调(控制全局)
* LUT贴图:可以从ps生成或者下载
* contribution:强度
5. Chromatic Aberration:色差
* Spectral Texture:色差贴图
* Intensity:强度
6. Water Effect:TGame定制的水下效果以及从水面上岸效果
* Mode:UnderWater是水下扭曲效果:
1. Intensity:扭曲程度;
2. Distortion SPeed: 扭曲速度
3. Distortion Tex:控制扭曲的贴图
* Mode:Wet Lens是镜头出水打湿效果:
1. 镜头打湿,法线扭动速度。可以通过脚本控制该值实现由干到湿的过程。
7. Out Line:场景中物体外发光效果
* Blur Size:外发光扩散大小
* Rt Scale: Render Texture大小,调到1是全屏,越大精度越高也越费
* Outter Color:外发光颜色

  1. Sky Scatter:大气散射效果
    • Resolution:分辨率,影响性能,选择low
    • Sun Color:颜色
    • Sun Shaft Intensity:大气散射强度
    • Max Radius:影响半径
    • Sample Radius:采样半径
  2. Color Grading:使用ToonMapping,色彩映射
    • ToonMapping:
      1. 通常被理解为从高动态范围(HDR)映射到低动态范围(LDR)的过程。在unity中,这意味着大多数平台上任意16位的浮点型颜色值将被映射为[0,1]范围内的传统8位颜色值。
      2. 只有在相机启用HDR时才能正常使用,建议给光源高于正常强度的亮度,从而让其具有更大的范围。
      3. 线性空间推荐ACES。Gamma空间Netural。
    • 调色调不建议使用这个,建议用User Lut调,比较方便

代码架构图

这里写图片描述

具体各个分析

bloom

主要分几部分:提取较亮区域生成较亮贴图,较亮贴图降采样。流程如下图所示:
这里写图片描述

shader

shader分为4个pass,前3个的pass顶点shader都一样是VertDefault,输出只包含屏幕坐标、uv,转换后的贴图坐标。第4个pass的顶点shader是VertMultitex,增加了对提取亮度的贴图uv坐标的平台兼容性的判断。4个pass的frag shader都不一样,分别是FragPrefilter,FragDownsample1,FragDownsample2,FragUpsample.第一个pass用来提取较亮区域;第二、三个pass用来降采样,两者的区别在于第二个pass考虑了抗闪烁的情况(宏判断),在downSample的时候用4*4 箱式过滤器和抗闪烁过滤,而第三个pass在downsample的时候只用4*4箱式过滤器;第四个pass进行升采样并进行颜色的叠加。
具体bloom逻辑在BloomComponent的prepare方法里。

vertDefault
struct AttributesDefault
{
    float4 vertex : POSITION;
    float4 texcoord : TEXCOORD0;
};

struct VaryingsDefault
{
    float4 pos : SV_POSITION;
    float2 uv : TEXCOORD0;
    float2 uvSPR : TEXCOORD1; // Single Pass Stereo UVs
};

VaryingsDefault VertDefault(AttributesDefault v)
{
    VaryingsDefault o;
    o.pos = UnityObjectToClipPos(v.vertex);
    o.uv = v.texcoord.xy;
    o.uvSPR = UnityStereoScreenSpaceUVAdjust(v.texcoord.xy, _MainTex_ST);
    return o;
}
顶点shader——VertMultitex

输入:

struct AttributesDefault
{
    float4 vertex : POSITION;
    float4 texcoord : TEXCOORD0;
};

输出

struct VaryingsMultitex
        {
            float4 pos : SV_POSITION;
            float2 uvMain : TEXCOORD0;
            float2 uvBase : TEXCOORD1;
        };

VaryingsMultitex VertMultitex(AttributesDefault v)
 {
      VaryingsMultitex o;
      o.pos = UnityObjectToClipPos(v.vertex);
      o.uvMain = UnityStereoScreenSpaceUVAdjust(v.texcoord.xy, _MainTex_ST);
      //UnityStereoScreenSpaceUVAdjust用于立体渲染准确定位那个摄像机,见https://docs.unity3d.com/Manual/SinglePassStereoRendering.html
      o.uvBase = o.uvMain;

  #if UNITY_UV_STARTS_AT_TOP //http://blog.csdn.net/qq_18229381/article/details/72466191
      if (_BaseTex_TexelSize.y < 0.0)
          o.uvBase.y = 1.0 - o.uvBase.y;
  #endif

      return o;
  }
提取亮度FragPrefilter
half4 FragPrefilter(VaryingsDefault i) : SV_Target
        {
            float2 uv = i.uv + _MainTex_TexelSize.xy * _PrefilterOffs;

        #if ANTI_FLICKER
            float3 d = _MainTex_TexelSize.xyx * float3(1.0, 1.0, 0.0);
            //FetchAutoExposed是对主贴图进行采样,并采样曝光度贴图,用曝光度贴图的r通道与主贴图的rgba相乘
            //safeHdr把颜色控制在hdr范围内;
            //如果开启了抗闪烁,采样周围四个采样点的颜色,减去最大最小得到颜色。
            half4 s0 = SafeHDR(FetchAutoExposed(_MainTex, uv));
            half3 s1 = SafeHDR(FetchAutoExposed(_MainTex, uv - d.xz).rgb);
            half3 s2 = SafeHDR(FetchAutoExposed(_MainTex, uv + d.xz).rgb);
            half3 s3 = SafeHDR(FetchAutoExposed(_MainTex, uv - d.zy).rgb);
            half3 s4 = SafeHDR(FetchAutoExposed(_MainTex, uv + d.zy).rgb);
            half3 m = Median(Median(s0.rgb, s1, s2), s3, s4);
        #else
            half4 s0 = SafeHDR(FetchAutoExposed(_MainTex, uv));
            half3 m = s0.rgb;
        #endif

        //如果是gamma空间,则要转换到线性空间。
        #if UNITY_COLORSPACE_GAMMA
            m = GammaToLinearSpace(m);
        #endif

            // Pixel brightness
            half br = Brightness(m);

            //提取较亮部分
            // Under-threshold part: quadratic curve
            half rq = clamp(br - _Curve.x, 0.   0, _Curve.y);
            rq = _Curve.z * rq * rq;

            // Combine and apply the brightness response curve.
            m *= max(rq, br - _Threshold) / max(br, 1e-5);

            return EncodeHDR(m);    
        }

脚本中_Curve参数如下: lthresh是设置的阈值
float knee = lthresh * bloom.softKnee + 1e-5f;
var curve = new Vector3(lthresh - knee, knee * 2f, 0.25f / knee);
material.SetVector(Uniforms._Curve, curve);

half br = Brightness(m);

        // Under-threshold part: quadratic curve
        half rq = clamp(br - _Curve.x, 0.0, _Curve.y);
        rq = _Curve.z * rq * rq;

        // Combine and apply the brightness response curve.
        m *= max(rq, br - _Threshold) / max(br, 1e-5);

        return EncodeHDR(m);
half4 EncodeHDR(float3 rgb)
{
#if USE_RGBM
    rgb *= 1.0 / 8.0;
    float m = max(max(rgb.r, rgb.g), max(rgb.b, 1e-6));
    m = ceil(m * 255.0) / 255.0;
    return half4(rgb / m, m);
#else
    return half4(rgb, 0.0);
#endif
}

float3 DecodeHDR(half4 rgba)
{
#if USE_RGBM
    return rgba.rgb * rgba.a * 8.0;
#else
    return rgba.rgb;
#endif
}
降采样FragDownsamp1
half4 FragDownsample1(VaryingsDefault i) : SV_Target
        {
        #if ANTI_FLICKER
            return EncodeHDR(DownsampleAntiFlickerFilter(_MainTex, i.uvSPR, _MainTex_TexelSize.xy));
        #else
            return EncodeHDR(DownsampleFilter(_MainTex, i.uvSPR, _MainTex_TexelSize.xy));
        #endif
        }

half3 DownsampleFilter(sampler2D tex, float2 uv, float
  • 5
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值