写点什么

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

作者:陈言必行
  • 2023-04-16
    辽宁
  • 本文字数:2389 字

    阅读完需:约 8 分钟

前言

在 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); }}
复制代码



二,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"}
复制代码


最后创建一个材质球使用上面创建的这个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); } }}
复制代码


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




2.4 实现效果

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


实现效果:



grayScaleAmount值调整到 1




效果展示

使用 Post Processing 的效果:



使用 Shade 的效果:



发布于: 刚刚阅读数: 2
用户头像

陈言必行

关注

公号:开发同学留步 2019-03-12 加入

我是一个从事Unity游戏开发攻城狮,InfoQ&阿里云签约博主,CSDN博客专家,U3D论坛版主,6年开发经验,助你日常不加班。⽂章皆为从零到⼀的⼊⻔级教程,也有很多⼯作中遇到的问题解析。

评论

发布
暂无评论
Unity 之 后处理实现界面灰度效果(PostProcessing实现 | Shader实现)_Unity_陈言必行_InfoQ写作社区