【Unity Shader】屏幕后处理1.0:调整亮度/饱和度/对比度

2023-11-19

1 Unity中实现屏幕特效的基本步骤

什么叫屏幕后处理(Screen post-processing effects)——渲染完整个场景得到屏幕图像后对图像进行一系列操作,实现各种屏幕特效,这一步我们可以添加很多例如景深(Depth of Field)、运动模糊(Motion Blur)等效果。

基本步骤大概分为:

  • 检查资源和条件是否满足(Pass掉)

《入门精要》中提到在进行后处理前需要判断当前平台是否支持渲染纹理和屏幕特效、使用的Unity Shader是否支持等等,实现这一步我们需要创建一个用于判断的基类脚本,后续所有进行后处理的脚本都需要继承这个基类。 

但C#脚本中用于检查的判断语法,我在真正实践时发现在Unity现在的版本中已经将它们舍弃了,因此我在做后处理练习时并没有完全按照《入门精要》上面的操作去执行它的基类,具体后面会有所体现。

  • 添加用于屏幕后处理的脚本

C#脚本和之前写的Shader文件不同,有了脚本文件Unity才会听懂哪个步骤是抓取屏幕图像、哪个步骤是对当前图像进行处理

  • 获取当前屏幕图像

这一步用Unity提供的OnRenderImage函数来实现

  • 使用Shader对图像进行处理

用Unity提供的Graphics.Blit函数实现

  • 把结果显示在屏幕上

2 准备脚本

2.1 省去了PostEffectsBase脚本

首先说一下《入门精要》中提前准备的一个PostEffectsBase的脚本,其中:

提示这两个最重要的判断语法都被弃用了,所以我认为之后的判断也都不是很有必要,所以就省去了PostEffectsBase这个前提脚本。 

2.2 Camera挂的脚本

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

[ExecuteInEditMode]
public class BrightnessSaturationAndContrast : MonoBehaviour
{
    public Shader briSatConShader;
    public Material briSatConMaterial;

    [Range(0.0f, 3.0f)]
    public float brightness = 1.0f;

    [Range(0.0f, 3.0f)]
    public float saturation = 1.0f;

    [Range(0.0f, 3.0f)]
    public float contrast = 1.0f;



    //运用 OnRenderImage(src, des)
    void OnRenderImage(RenderTexture source, RenderTexture destination)
    {
        if (briSatConMaterial != null)
        {
            briSatConMaterial.SetFloat("_Brightness", brightness);
            briSatConMaterial.SetFloat("_Saturation", saturation);
            briSatConMaterial.SetFloat("_Contrast", contrast);
            Graphics.Blit(source, destination, briSatConMaterial);
        }
        else
        {
            Debug.LogWarning("Please input your Material!");
            Graphics.Blit(source, destination);
        }

    }
}

其中涉及到了两个第1小节已经提到过的重要函数:

OnRenderImage函数

关于该函数的用法,官方介绍: 

MonoBehaviour-OnRenderImage(RenderTexture,RenderTexture) - Unity 脚本 API

第一个参数是当前屏幕的渲染纹理/上一步处理后得到的渲染纹理,第二个参数是目标渲染纹理,也是最后输出在屏幕上的东西。

Graphics.Blit函数

这个函数通常都会包括在一个OnRenderImage函数里,官方介绍:

Graphics-Blit - Unity 脚本 API

它有超多种形式

这次用的是第二种,

  • mat——是我们使用的材质,材质的Shader就是真正进行后处理操作的部分
  • pass——表示将会依次调用Shader中的所有Pass

3 最重要的Shader部分

首先,亮度、饱和度、对比度这三个值其实是数字图像处理中常见属性,其他常见的还有锐化、分辨率等等属性。,图像的亮度、饱和度、对比度和锐化之间不是相互独立的。之前进行色彩基础学习的时候总结了一点点色彩相关的东西:【技术美术图形部分】2.1 色彩空间

参考:

【数字图像处理系列二】基本概念:亮度、对比度、饱和度、锐化、分辨率

3.1 亮度部分

亮度是这三个参数里最好理解的,就是颜色的明亮程度,它直接跟RGB三个分量的大小相关,所以RGB值越大,颜色亮度也越大。因此调整亮度很简单,直接在采样得到的像素R/G/B分别×亮度修改值Brightness就行:

//亮度
fixed3 finalColor = renderTex.rgb * _Brightness;

3.2 饱和度部分

饱和度是指图像颜色种类的多少、色彩的纯度,通俗一点将也就是颜色的鲜艳程度,他也是HSV(hue-saturaion-value)色彩模型中包含的色相、饱和度、明度三个属性之一,调整饱和度可以修正过度曝光/未充分曝光的照片,让图片看上去更加自然。 

下面是Shader中调整饱和度的代码部分:

//饱和度
fixed luminance = 0.2125 * renderTex.r + 0.7154 * renderTex.g + 0.0721 * renderTex.b;  //计算该像素的亮度值
fixed3 luminanceColor = fixed3(luminance, luminance, luminance);  //创建饱和度为0的颜色
finalColor = lerp(luminanceColor, finalColor, _Saturation);

这是在通过RGB三通道的值转灰度图。由于人眼对红绿蓝的敏感程度不同,所以计算灰度时需要根据R/G/B三个通道的值进行加权平均。

那么(0.2125, 0.7154, 0.0721)怎么得到的这三组值到底是怎么来的?搜到的说这三组值是YUV BT709中的RGB转YUV的算法,而RGB转YUV的算法有很多种。简单了解一下YUV:YUV是一种颜色编码方法,其中

  • Y表示明度(Luminance/Luma),即灰阶值
  • U和V表示色度,作用是描述色彩及饱和度,用于指定像素的颜色

下面这是搜到的网上BT709的RGB和YUV互相转的算法: 

上面Shader代码中改变饱和度的方法是先获得灰度图(三通道都取计算的Y灰阶值),然后用这个灰度颜色跟上一步修改亮度之后的颜色中间做插值,插值的依据就是用户输入的_Saturation的调整值。

3.3 对比度部分

对比度指的是图像暗和亮的落差值,也是图像最大灰度和最小灰度之间的差值。通俗一点讲,对比度越高,图像越清晰;对比度越小,图像越灰蒙蒙的。

对比度和亮度的区别就在于,亮度表达的是图像的黑白占比,亮度越大整体趋向于白色,亮度越小趋向于黑色;而对比度这个考虑的是图像中最亮和最暗之间的差距,灰色是对比度为0的归宿,而整个画面黑白超级明显则是对比度为1呈现的效果。

代码中的实现也很简单:

//contrast
fixed3 avgColor = fixed3(0.5, 0.5, 0.5);
finalColor = lerp(avgColor, finalColor, _Contrast);

先取一个对比度为0的颜色值(R/G/B均为0.5),Unity对于RGBA的取值可参考UnityEngine.Color - Unity 脚本 API),然后也是用一个插值范围函数lerp将用户输入的对比度控制值_Contrast包含进去。 

3.4 完整代码

Shader "Unity Shaders Book/Chapter 12/BrightnessSaturationAndContrast"
{
    Properties
    {
        _MainTex ("Base(RGB", 2D) = "white" {}
        //从脚本传递更好,这里可以直接省略这些值的展示
        _Brightness ("Brightness", float) = 1
        _Saturation ("Saturation", float) = 1
        _Contrast ("Contrast", float) = 1
    }
    SubShader
    {
        Pass
        {
            //关闭深度写入
            ZTest Always Cull Off Zwrite Off

            CGPROGRAM

            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"
            
            //properties
            sampler2D _MainTex;
            half _Brightness;
            half _Saturation;
            half _Contrast;

            struct v2f {
                float4 pos : SV_POSITION;
                half2 uv : TEXCOORD0; 
            };

            //使用了内置的appdata_img结构体作为顶点着色器的输入
            v2f vert(appdata_img  v) {
                v2f o;
                o.pos = UnityObjectToClipPos(v.vertex);
                o.uv = v.texcoord;
                return o;
            }

            fixed4 frag(v2f i) : SV_Target {
                //获得屏幕图像的采样
                fixed4 renderTex = tex2D(_MainTex, i.uv);

                //亮度
                fixed3 finalColor = renderTex.rgb * _Brightness;

                //饱和度
                fixed luminance = 0.2125 * renderTex.r + 0.7154 * renderTex.g + 0.0721 * renderTex.b;  //计算该像素的亮度值
                fixed3 luminanceColor = fixed3(luminance, luminance, luminance);  //创建饱和度为0的颜色
                finalColor = lerp(luminanceColor, finalColor, _Saturation);

                //contrast
                fixed3 avgColor = fixed3(0.5, 0.5, 0.5);
                finalColor = lerp(avgColor, finalColor, _Contrast);

                return fixed4(finalColor, renderTex.a);
            }
            ENDCG
        }
    }
    FallBack  Off
}

4 效果展示

这里我用了一个大家都会用到的Unity商城里的免费资源,给Main Camera挂上了上面写的脚本,效果如下:

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【Unity Shader】屏幕后处理1.0:调整亮度/饱和度/对比度 的相关文章

  • unity: C#的Action Event Delegate的异同

    目录 一 Action 二 Event 三 Action和Event区别 四 Delegate 总结 Action Event Delegate的异同 前言 Action Event和Delegate都是C 语言中的重要概念 分别用于管理函
  • Unity打包WebGL的优化常用操作?

    1 贴图部分优化 如果贴图格式时2048 在不影响画面效果的情况下 改成1024或者5 12 还可以缩小包体 2 压缩和解压缩问题 WebGL打包的时候分三种压缩情况 gzip 比Brotli文件打 但打包快 http和https都支持 B
  • visual studio 一直显示正在准备解决方案

    首先重启电脑 无法解决的情况下执行以下步骤 Kill Visual Studio Open Visual Studio without loading a solution Disable AnkhSvn as Source Control
  • cocos creator学习笔记 十分详细

    游戏系统 分辨率及适配 当屏幕分辨率与设计分辨率不同时会出现适配问题 widget 对节点实现自动定位 运行原理 生命期回调 onLoad 回调会在节点首次激活时触发 比如所在的场景被载入 或者所在节点被激活的情况下 通常我们会在 onLo
  • 《开箱元宇宙》:Madballs 解锁炫酷新境界,人物化身系列大卖

    你是否曾想过 元宇宙是如何融入世界上最具代表性的品牌和名人的战略中的 在本期的 开箱元宇宙 系列中 我们与 Madballs 的战略顾问 Derek Roberto 一起聊聊 Madballs 如何在 90 分钟内售罄 2 000 个人物化
  • 华为OD机试真题-游戏分组-2023年OD统一考试(C卷)

    题目描述 部门准备举办一场王者荣耀表演赛 有10名游戏爱好者参与 分为两队 每队5人 每位参与者都有一个评分 代表着他的游戏水平 为了表演赛尽可能精彩 我们需要把10名参赛者分为实力尽量相近的两队 一队的实力可以表示为这一队5名队员的评分总
  • unity小球跟随音乐节奏放大缩小和改变颜色

    放在小球身上 设置对应组件即可 using System Collections using System Collections Generic using Unity VisualScripting using UnityEngine
  • python趣味编程-使用 Python 的 Tron 游戏(多人游戏)

    使用 Python 的 Tron 游戏 多人游戏 和免费源代码 带有源代码的Tron Game 多人游戏 是一款多人游戏 您的目标是创建更长的线 你只需要小心移动 tron 车辆即可 该项目的目的是为您的亲戚和朋友提供一些娱乐因素 关于该项
  • 利用阿里云的尖端数据库解决方案增强游戏数据管理

    在快节奏和动态的游戏世界中 对于努力为玩家提供无缝体验的公司来说 管理大量数据是一项关键挑战 阿里云是亚太地区的主要参与者 也是全球公认的运营数据库管理系统领导者 提供量身定制的创新解决方案 以应对游戏公司面临的独特数据管理挑战 这篇博客探
  • Metasequoia4 for Mac/win:带您进入三维模型游戏建模的新世界!

    在当今游戏行业中 三维模型的重要性无可置疑 而在这个充满创造力和无限可能的领域中 Metasequoia4 简称M4 无疑是您的最佳选择 作为一款专业的三维模型游戏建模软件 M4凭借其强大的功能和简便易用的界面 成为了无数游戏开发者和设计师
  • 全面解析找不到xinput1_3.dll无法继续执行代码的多种解决方案(实用教程)

    xinput1 3 dll文件是什么 xinput1 3 dll是一个动态链接库文件 它是DirectInput的组件之一 DirectInput是微软公司开发的一种输入设备驱动程序 用于处理游戏控制器 键盘 鼠标等输入设备的信号 xinp
  • 计算机提示vcruntime140.dll丢失的解决方法,多种修复教程分享

    vcruntime140 dll是一个非常重要的动态链接库文件 它包含了许多运行时的函数和类 然而 有时候我们可能会遇到vcruntime140 dll无法继续执行代码的问题 这会给我们带来很大的困扰 那么 这个问题是什么原因导致的呢 又应
  • Unity中URP下的指数雾

    文章目录 前言 一 指数雾 雾效因子 1 FOG EXP 2 FOG EXP2 二 MixFog 1 ComputeFogIntensity 雾效强度计算 2 lerp fogColor fragColor fogIntensity 雾效颜
  • 盘点第三方支付行业中一些“专用术语”

    每个行业都有自己的行业术语 在官方场合被称为 术语 如果你是支付行业的新手 或者正在努力深入了解这个复杂但充满机遇的领域 那么掌握行业术语就是打开这扇门的关键 支付系统的需求文档和技术方案文档都充斥着专业词汇和行业术语 有些词汇容易理解 有
  • 广告竞价策略:激发广告变现潜能的关键

    在数字化时代 广告已经成为企业推广品牌 产品和服务的关键手段之一 为了最大程度地发挥广告的效果 广告竞价策略成为广告主和数字营销专业人士关注的焦点 通过巧妙运用竞价策略 广告主可以在激烈的市场竞争中脱颖而出 实现广告变现的潜能 admaoy
  • 基于java的扫雷游戏系统设计与实现

    基于java的扫雷游戏系统设计与实现 I 引言 A 研究背景和动机 扫雷游戏是一种经典的益智游戏 由于其简单易学 规则简单 玩法多样等特点 深受广大游戏爱好者的喜爱 但是 现有的扫雷游戏系统往往存在着游戏难度不均衡 游戏时间过长 游戏规则不
  • U3D游戏开发中摇杆的制作(UGUI版)

    在PC端模拟摇杆 实现玩家通过控制摇杆让玩家移动 以下是完整代码 using System Collections using System Collections Generic using UnityEngine using Unity
  • U3D游戏开发中摇杆的制作(NGUI版)

    在PC端模拟摇杆 实现控制摇杆让玩家或者物体移动 以下是完整代码 using System Collections using System Collections Generic using UnityEngine public clas
  • 游戏开发常见操作梳理系列之——玩家信息的显示系统

    在游戏中 有不少游戏在左上角会出现玩家的头像和等级以及血量 这就是玩家的信息显示系统 那么这些是如何制作的呢 接下来我将讲讲代码的操作 其它操作我会在其它笔记中一一说明 敬请期待 信息的显示相当简单就是控制一些UI 然后在其它系统里面填写相
  • 游戏开发常见操作梳理之NPC药品商店系统(NGUI版)

    后续会出UGUI Json的版本 敬请期待 游戏开发中经常会出现药品商店 实际操作与武器商店类似 甚至根据实际情况可以简化设置 废话不多说 直接上代码 药品商店的源码 using System Collections using Syste

随机推荐

  • java复制文件后保持文件的创建时间不变

    复制后保持文件的创建时间不变 File oldFile new File E test old png File newFile new File E test new png FileCopyUtils copy oldFile newF
  • 【JAVA

    package learn import javax swing public class SimpleTable JFrame jf new JFrame 简单表格 String titles 姓名 性别 职业 String data 李
  • C语言数据变量

    1 变量的创建 上篇文章我们了解清楚了数据的类型 我们使 类型做什么呢 在C语言中 变量的创建包括变量的声明和变量的定义 变量的声明是指在程序中说明变量的存在 告诉编译器变量的类型和名称 变量的声明通常放在函数的头部或全局变量的前面 例如
  • 算法,16瓶水,有一瓶有毒,假设一只小白鼠喝一滴水,一个小时后会死亡,一个小时找出那瓶有毒的水至少需要几只小白鼠?

    首先16瓶水 编号0000 0001 1110 1111 然后让第一只小白鼠喝最低位为1的水 第二只小白鼠喝次最低位为1的水 第三只小白鼠喝第三位为1的水 第四只小白鼠喝最高位为1的水 一个小时后看小白鼠的存活状态 若小白鼠全活则0000
  • 找出通过车辆最多颜色(90%用例)

    在一个狭小的路口 每秒只能通过一辆车 假如车辆的颜色只有3种 找出N秒内经过的最多颜色的车辆数量 三种颜色编号为0 1 2 输入描述 第一行输入的是通过的车辆颜色信息 0 1 1 2 代表4秒钟通过的车辆颜色分别是0 1 1 2 第二行输入
  • COCOS2DX学习之Box2D物理引擎-------物体和相互作用

    1 创建一个静态物体 创建一个静态物体应该很简单 在头文件生命一下要创建新物体的函数 然后在cpp文件中实现它即可 具体的时候先过程 首先要用createbody函数创建一个物体 然后定义一个b2bodydef变量 指定一下这个变量的typ
  • 游戏开发unity编辑器扩展知识系列:修改纹理资源的TextureType

    需要用TextureImporter导入资源 调用如下代码 TextureImporter importer TextureImporter TextureImporter GetAtPath path importer textureTy
  • ADC 读取电位器旋钮,用回差消除临界值档位跳动

    就是比如 用电位器当旋钮做风扇调速 划分出10 个速度档位 对应10 个ADC 转换结果的阈值 如果直接比较阈值 当旋钮拧到临近阈值的地方时 ADC 结果的微小跳动会导致风扇档位在两个级别之间不停左右横跳 因此想到了利用回差来消除抖动 回差
  • Pycharm官网下载安装

    下载链接 pycharm官网 https www jetbrains com pycharm 然后来到这个界面 点击Download 下载按钮 然后点击开源版本 Community 下载安装就好了 接下来就创建项目 点击Create 这样就
  • FISCO BCOS 2.0新特性解读

    FISCO BCOS是完全开源的联盟区块链底层技术平台 由金融区块链合作联盟 深圳 简称金链盟 成立开源工作组通力打造 开源工作组成员包括博彦科技 华为 深证通 神州数码 四方精创 腾讯 微众银行 亦笔科技和越秀金科等金链盟成员机构 代码仓
  • Nacos、ZooKeeper和Dubbo的区别

    Nacos ZooKeeper和Dubbo是三个不同的分布式系统组件 它们之间有以下几点区别 功能定位 Nacos主要提供服务发现 配置管理和服务治理等功能 而ZooKeeper主要是分布式协调服务 提供了分布式锁 分布式队列等原语 Dub
  • 本地部署LLaMA-中文LoRA部署详细说明

    在Ubuntu18 04 部署中文LLaMA模型 环境准备 硬件环境 AMD 5950X 128GB RAM RTX 3090 24G VRAM 操作系统 Ubuntu 18 04 编译环境 可选 llama cpp 编译 cd llama
  • GoJS学习

    简介 GoJS是一个可视化JavaScript库 用于浏览器中创建交互图形 比如流程图 树图 关系图 力导图等等 GoJS不依赖于任何JS库或框架 例如bootstrap jquery等 可与任何HTML或JS框架配合工作 甚至可以不用框架
  • Cuda 代码中的 函数前缀 device global host 使用

    众所周知 CUDA并行可以使代码加速很多倍 其文件类型为 cu 结尾 在编写cu 文件时 常用的函数前缀关键字有 device global host host C或者C 中相同 是由CPU调用 由CPU执行的函数 global 表示一个内
  • 为啥国内互联网公司都用centos而不是ubuntu?

    一直以来都很好奇ubuntu和centos有啥区别 上学时接触的都是ubuntu 自己每次装virtual box的时候都会下个ubuntu 但是公司的服务器上装的都是centos 今天查了下知乎网友的精彩回答 呵呵 简单总结下主要有几个原
  • 解释执行与编译执行语言有什么区别?

    一 主体不同 1 编译执行 由编译程序将目标代码一次性编译成目标程序 再由机器运行目标程序 2 解释执行 将源语言直接作为源程序输入 解释执行 解释一句后就提交计算机执行一句 并不形成目标程序 二 优势不同 1 编译执行 相比解释执行编译执
  • 常用的偏微分方程

    偏微分方程通常包含两个以上的自变量 若自变量同时间相关 或者无关 称其为发展型 或者稳态 的 下面 我们罗列出一些典型的偏微分方程 如 热传导方程 一阶双曲守恒律方程 二阶波动方程 椭圆型偏微分方程等 抛物型偏微分方程通常刻画 个物理系统的
  • 前端学科面试题大全

    作用域和值类型引用类型的传递 变量作用域 作用域变量访问区域 变量值存在栈中 变量赋值相当于值赋值 值传递与引用传递有哪些区别 函数内部 变量会先声明 形式参数变量声明提升 整个函数体有var声明的变量 如果没有访问全局定义的num2 函数
  • 服务器环境初始化配置

    工程实践经验积累 服务器环境初始化配置 1 新建环境 新建环境 是为了使自己的程序在一个相对独立的环境中运行 不影响服务器上其他用户 并不受其他用户影响 新建环境的语句为 conda create n your env name pytho
  • 【Unity Shader】屏幕后处理1.0:调整亮度/饱和度/对比度

    1 Unity中实现屏幕特效的基本步骤 什么叫屏幕后处理 Screen post processing effects 渲染完整个场景得到屏幕图像后对图像进行一系列操作 实现各种屏幕特效 这一步我们可以添加很多例如景深 Depth of F