百度360必应搜狗淘宝本站头条
当前位置:网站首页 > IT知识 > 正文

零基础带你看游戏内灰度效果实现原理

liuian 2025-05-08 02:45 30 浏览

前言

在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的效果:

相关推荐

基于STM32的四旋翼飞行器控制系统设计

摘要:四旋翼飞行器控制系统的性能决定了飞行效果的优劣,如何改善飞行控制系统使其拥有更良好的表现成为近几年的研究热点。根据四旋翼飞行器的飞行原理,设计了一种新型四旋翼飞行器控制系统。该系统以STM32...

单片机差分升级(STM32,M0,M3,M4适用)

参考文档:https://blog.csdn.net/darling757267/article/details/80652267https://www.cnblogs.com/idreamo/p/9...

STM32入门: Step3 UART简介(stm32的uart和usart)

Step3UART简介UART和新板介绍34分钟目标描述完成本教程后,您将:熟悉L475IoTNodeDiscovery板,了解如何在以下位置对RS232串行链路进行编程和使用:以前使...

如何使用GCC手动编译stm32程序(如何在gcc上编译并运行代码)

如何不使用任何IDE(集成开发环境)编译stm32程序?集成开发环境将编辑器、编译器、链接器、调试器等开发工具集成在一个统一的软件中,使得开发人员可以更加简单、高效地完成软件开发过程。如果我们不使用K...

STM32单片机从零开始使用教程(二) 使用Cube搭建跑马灯工程并下载

通过cube建立工程RCC设置为外部晶振高速晶振对应的引脚会亮起进入clockconfiguration进行时钟配置,输入频率8M盒子PLLSourceMu改为使用外部时钟HSE,系统时钟Sys...

超详细的FreeRTOS移植全教程——基于stm32

准备在移植之前,我们首先要获取到FreeRTOS的官方的源码包。这里我们提供两个下载链接:一个是官网:http://www.freertos.org/另外一个是代码托管网站:https://sourc...

用ESP32和STM32设计了一块主控板,却用来养鱼?

前言我做了一个智能鱼缸系统的控制板。基于ESP32和STM32设计。成本不到200元。全文导航功能描述、电路设计图、主要模块选型、软件说明、获取开源资料、结语。功能描述①自动投食。②自动过滤供氧。③灯...

STM32物联网套件基础版03-控制继电器

前言继电器是一个生活中比较常用的元器件,有了继电器,我们可以使用单片机输出的低电平控制高电平期间工作,比如继电器接到220V用电器上,可通过单片机智能控制用电器,本节我们开始正式学习如何使用继电器。一...

STM32单片机详细教学(三):STM32单片机的开发方法

大家好,今天给大家介绍STM32单片机的开发方法,文章末尾附有本毕业设计的论文和源码的获取方式,可进群免费领取。前言经过前两章节对STM32的简单介绍,在接下来的几个章节中开始进行STM32单片机的软...

原来STM32单片机的开发如此的简单

大家好,我是华维今天我们讲下用STM32CubeMX和Keil5点亮一个LED,这个项目比较简单,大家都可以尝试下。这个就是我们今天的主角,这款单片机芯片是STM32F030K6T6。这个开发板非常简...

STM32 F103 使用HAL库配置PVD(stm32l0 hal库)

PVD(ProgrammableVotageDetector),即可编程电压监测器PVD可以检测电压变化并触发中断,一般用于判断断电并进行数据保存工程是使用STM32CubeMx生成的,在ST...

STM32F4芯片嵌入式学习7(stm32f4芯片包安装)

文档是本人学习总结的文档,有些乱,勿怪。1、STM32F407时钟系统在STM32F4中,有5个最重要的时钟源,为HSI、HSE、LSI、LSE、PLL。其中PLL实际是分为两个时钟源,...

ARM和STM32,嵌入式是什么关系(arm嵌入式和单片机的区别)

ARM,STM和嵌入式的关系就是上图,其中ARM负责内核架构,指令集的设计,提供给IC设计厂商内核和编译器等支持(如ARM11,Cortex-M,Cortex-A系列处理器)STM32是基于ARM...

STM32CUBEMX 使用教程2 — GPIO的使用、输入/输出

学习和使用任何一款MCU,最初开始往往都是从GPIO入手的,学会如何配置IO,能让IO输出想要的电平状态,能读取IO口的电平。本篇介绍一下STM32的GPIO。GPIO(General-Purpose...

入手STM32单片机的知识点总结(stm32单片机的介绍)

文章下方附学习资源,自助领取本文将以STM32F10x为例,对标准库开发进行概览。主要分为三块内容:STM32系统结构寄存器通过点灯案例,详解如何基于标准库构建STM32工程STM32系统结构上图,S...