• Unity 之 后处理实现界面灰度效果(PostProcessing实现 | Shader实现)


    前言

    在Unity中实现后处理效果有两种方式:一种是通过使用Unity官方提供的Post-Processing插件。另外一种方式就是使用脚本获取到渲染后帧缓冲区的图像,再通过shader写后处理的效果,最后合并输出图像到屏幕上。


    一,Post Processing

    1.1 设置组件

    Post Processing后处理插件的工程配置:Unity 之 Post Processing后处理不同项目配置(UPR项目配置)

    这里我们用到的只有:Color Grading 色彩调整

    此组件可以使一个用到的特效,用于校正镜头中的颜色与亮度,来实现电影或海报效果,可以理解为手机中的何种滤镜。

    这个组件设置项很多,此篇文章中介绍的灰度效果只需要使用到一个,那就是:Saturation 饱和度

    Saturation值调整为-100,则可以实现灰度效果。随便大家一个界面测试一下:


    1.2 代码控制

    我这里创建了一个Slider,来动态控制Saturation 饱和度的值。创建代码命名为PostProcessingProfile 将其挂载到场景中PostProcessProfile的物体上,就可以实现开篇看到的滑动条控制灰度效果了:

    具体代码如下,

    using UnityEngine;
    using UnityEngine.Rendering.PostProcessing;
    using UnityEngine.UI;
    
    public class PostProcessingProfile : MonoBehaviour
    {
        public Slider Slider_Profile;
        
        // 后处理的配置容器
        private PostProcessVolume _volume;
        // 根据Inspector面板上的组件创建对应类型变量
        private ColorGrading _colorGrading;
        
        void Start()
        {
            // 获取容器
            _volume = GetComponent<PostProcessVolume>();
            // 获取此容器下添加的组件
            _volume.profile.TryGetSettings(out _colorGrading);
            
            Slider_Profile.onValueChanged.AddListener(OnValueChangedSlider);
        }
    
        void OnValueChangedSlider(float progress)
        {
            Debug.Log(progress + " " + (progress * 100 - 100));
            // 控制灰度值的变化 (-100 0 -> 0-1)
            _colorGrading.saturation.Override(progress * 100 - 100);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    二,Shader材质实现

    2.1 原理API

    Unity在摄影机完成渲染后调用的事件函数,允许您修改摄影机的最终图像。

    Unity后处理OnRenderImage

    在内置渲染管道中,Unity在摄影机完成渲染后,调用MonoBehaviours上的OnRenderImage,该MonoBeh aviours作为已启用的摄影机组件附加到同一GameObject。您可以使用OnRenderImage创建全屏后处理效果。这些效果通过从源图像读取像素,使用Unity着色器修改像素的外观,然后将结果渲染到目标图像中来实现。通常使用图形。Blit执行这些步骤。

    Graphics.Blit 官方文档:使用着色器将源纹理复制到目标渲染纹理。
    Blit将dest设置为渲染目标,在材质上设置source_MainTex属性,并绘制全屏四边形。

    若要blit到内置渲染管道中的屏幕后缓冲区,必须确保dest为空,并且摄影机为空。Camera的targetTexture属性。main也为空。如果dest为空,Unity将尝试使用Camera.main.targetTexture作为目标。


    2.2 编写Shader

    当输出的RGB值相同的时候,就变成了灰度图。所以只要在渲染输出颜色值的地方,将RGB输出为相同的值就可以实现了。

    实现相同值的我们一般使用是加权平均值的做法,目前最常用的一组灰色RGB值是(0.299,0.587,0.114)。

    具体怎么来的我也算不明白,感兴趣的同学可以搜索:在使彩色图变灰RGB的权重会固定为(R:0.299 G:0.587 B:0.114)? 学习一下。

    最终Shader如下:

    Shader "Custom/GrayAll"
    {
        Properties
        {
            _MainTex("Base (RGB)", 2D) = "white" {}
            // 灰度值
            _Color("GaryScale Amount", Range(0.0, 1.0)) = 1.0
     
        }
     
        SubShader
        {    
            Pass{
                CGPROGRAM
                #pragma vertex vert_img
                #pragma fragment frag
                
                #include "UnityCG.cginc"
                
                uniform sampler2D _MainTex;
                fixed _Color;
         
                fixed4 frag(v2f_img i) : COLOR
                {
                    fixed4 renderTexture = tex2D(_MainTex, i.uv);
                    
                    float grayValue = 0.299 * renderTexture.r + 0.587 * renderTexture.g + 0.114 * renderTexture.b;
                    
                    fixed4 finalColor = lerp(renderTexture, grayValue, _Color);
                    
                    return finalColor;
                }        
                ENDCG
            }
        }
        FallBack "Diffuse"
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    最后创建一个材质球使用上面创建的这个Shader,待下面使用:


    2.3 编写代码

    创建代码SetGrayScene,并将其挂载到摄像机上,通过2.1中介绍的OnRenderImageGraphics.Blit实现,具体代码如下:

    using UnityEngine;
    
    public class SetGrayScene : MonoBehaviour
    {
        public Material grayMaterial;
        [Range(0f,1f)]
        public float grayScaleAmount = 1.0f;
        
        void Start()
        {
            // 判断终端是否支持
            if (grayMaterial != null && grayMaterial.shader.isSupported == false)
            {
                enabled = false;
            }
        }
    
        void OnRenderImage(RenderTexture sourceTexture, RenderTexture destTexture)
        {
            if (grayMaterial != null)
            {
                grayMaterial.SetFloat("_Color", grayScaleAmount);
                Graphics.Blit(sourceTexture, destTexture, grayMaterial);
            }
            else
            {
                Graphics.Blit(sourceTexture, destTexture);
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    最后将2.2中创建的材质球,赋值给挂到摄像机的脚本上:


    2.4 实现效果

    运行游戏,调整grayScaleAmount值即可看到效果。

    实现效果:

    grayScaleAmount值调整到1


    效果展示

    使用Post Processing的效果:

    使用Shade的效果:

  • 相关阅读:
    快速上手SpringBoot继承SpringSecurity(安全框架)详细教程!
    关于华为笔记本谷歌浏览器(chrome)默认启动页为360搜索的解决方法
    宝塔配置tomcat
    Kubeadm部署k8s集群 && kuboard
    GcExcel5.2.3 中文版-Documents for Excel Java
    正雅齿科儿童咬合诱导及青少年隐形矫治线上线下融合峰会成功开展
    MineMine 算法(1)
    VUE-----vue路由
    Kubernetes(k8s)访问控制:身份认证
    Linux系列之重定向操作
  • 原文地址:https://blog.csdn.net/Czhenya/article/details/128148123