目录

  • 1.灰度化是什么
  • 2.灰度值计算方法
  • 3.灰度化的目的
  • 4.Shader实现
  • 5.参考文章

在《OpenCV for Unity学习笔记(1)——Mat及灰度化图像》我们看到,使用OpenCV进行图像灰度化很简单,直接调用Imgproc.cvtColor更改图像的颜色空间即可。

Imgproc.cvtColor(mat, grayMat, Imgproc.COLOR_BGR2GRAY);

但是这个方法背后的原理是什么呢?
我们今天就来回答这个问题,并在Unity中用Shader来实现。

1.灰度化是什么

灰度化就是将一幅彩色图像转换为灰度图像的过程。
彩色图像通常使用的是RGB(红绿蓝)颜色模型,即图像上每一个像素点的颜色都是由这三种颜色混合而成。
这里会有几个问题:
①为什么是红绿蓝不是橙白紫呢?
因为红绿蓝是计算机中的三原色,就是红绿蓝这三种颜色不能够再分解为其他多种颜色的组合,而其他任何颜色都可以用这三种颜色组合而成。
②我们用多大的数字或者说计算机用多少位来表示RGB每个分量?用8位(0~255),还是16位(0 ~ 65535),还是更高?
RGB各分量的值一般用0~255表示,因为每个分量可表示的颜色为256种,那么三种颜色混合起来就能表示出256 * 256 * 256 =16777216 (1600多万) 种颜色,而我们人眼实际上分辨不出来那么细致的颜色,所以完全够用了。
RGB分量这个分量我们又称之为通道
除RGB通道之外,对于透明图片还有个A(alpha,阿尔法)通道,用于表征该像素点的透明度。
如果一张图片只包含RGBA中的一个,我们就说这张图片是单通道图片,也就是说每个像素点只占8位;
如果一张图片只包含RGB,我们就说这张图片是三通道图片,也就是说每个像素点占24位;
如果一张图片包含RGBA,我们就说这张图片是四通道图片,也就是说每个像素点占32位;
当然我们在shader中使用图片时,不一定非要按照RGBA就对应红绿蓝、透明度的格式来使用,在RGBA通道我们可以存储任何我们想存的信息。

比如我们可以把高光反射的强度存储在R通道,把边缘光照的强度存储在G通道,把高光反射的指数部分存储在B通道,最后把自发光的强度存储在A通道。
出处:《Unity Shader入门精要》 7.4.2节 其他遮罩纹理

此外,我们知道了图像的通道数和分辨率就能计算出这张图像占的内存大小。比如下面这张jpg图片,分辨率为474×296,三通道,那此张图片占用内存 = 474×296×3÷1024=411.04Kb。

我们计算的和Unity中展示的相同。

但是这张图片保存在硬盘上只占26.5Kb,为什么呢?这是因为图片保存的格式是.jpg的,这种格式是一种压缩格式,所以在硬盘上的大小比内存中会小一些。

说了这么多,跟灰度化没多大关系,捂脸。言归正传,看看灰度化
灰度化其实很简单,把RGB各个通道的值全部变成相同即可。
这个值我们又称之为灰度值
那么问题来了,灰度值该怎么计算?

2.灰度值计算方法

目前常用的方式有如下4种:

  • 分量法,取RGB中仍一分量的值
  • 最大值法,取RGB中的最大值
  • 平均值法,RGB三份量取平均值,(R+G+B)/3
  • 加权平均法,该方法根据重要性及其它指标,将三个分量以不同的权值进行加权平均。由于人眼对绿色的敏感最高,对蓝色敏感最低,因此,按下式对RGB三分量进行加权平均能得到相对合理的灰度图像。灰度值 = 0.299R+0.578G+0.114*B

3.灰度化的目的

前面说到将彩色图像转化为灰度图像的过程是图像的灰度化处理过程。彩色图像中的每个像素的颜色由R,G,B三个分量决定,而每个分量中可取值0-255,这样一个像素点可以有1600多万(256256256=16777216)的颜色的变化范围。而灰度图像是R,G,B三个分量相同的一种特殊的彩色图像,其中一个像素点的变化范围为256种,所以在数字图像处理中一般将各种格式的图像转化为灰度图像,使得后续的图像的计算量少一些。灰度图像的描述与彩色图像一样仍然反映了整副图像的整体和局部的色度和高亮等级的分布与特征。现在大部分的彩色图像都是采用RGB颜色模式,处理图像的时候,要分别对RGB三种分量进行处理,实际上RGB并不能反映图像的形态特征,只是从光学的原理上进行颜色的调配。图像灰度化处理可以作为图像处理的预处理步骤,为之后的图像分割、图像识别和图像分析等上层操作做准备。

4.Shader实现

原理弄清楚了就该写代码了。
我们这里要在UI上实现灰度化,那问题来了,Unity上UI的shader该怎么写?
Unity内置的Shader都是开源的,咱们可以在https://unity.cn/releases下载对应版本的内置Shader源码。

那么我们只需要在内置Shader的基础上更改就行了。
在Unity中我们创建一张Image,会发现它使用的默认Shader是UI/Default。

然后我们打开下载的内置Shader,找到在DefaultResourcesEctra》UI下找到UI-Default这个Shader,用记事本打开。

然后在Unity中新建一个Shader,将UI-Default的内容完整拷贝到我们新建的Shader中。然后开始愉快的灰度化图像。
这里,我们采用加权平均值法。
即,灰度值 = 0.299×R + 0.587×G + 0.114×B。
我们只需要在片元着色器末尾加上以下代码即可:

fixed gray = dot(color.rgb, fixed3(0.299, 0.587, 0.114));
color = half4(gray, gray, gray, color.a);


效果如下。

博主本文博客链接。

完整Shader如下.

// Unity built-in shader source. Copyright (c) 2016 Unity Technologies. MIT license (see license.txt)Shader "Custom/UI/Gray"
{Properties{[PerRendererData] _MainTex ("Sprite Texture", 2D) = "white" {}_Color ("Tint", Color) = (1,1,1,1)_StencilComp ("Stencil Comparison", Float) = 8_Stencil ("Stencil ID", Float) = 0_StencilOp ("Stencil Operation", Float) = 0_StencilWriteMask ("Stencil Write Mask", Float) = 255_StencilReadMask ("Stencil Read Mask", Float) = 255_ColorMask ("Color Mask", Float) = 15[Toggle(UNITY_UI_ALPHACLIP)] _UseUIAlphaClip ("Use Alpha Clip", Float) = 0}SubShader{Tags{"Queue"="Transparent""IgnoreProjector"="True""RenderType"="Transparent""PreviewType"="Plane""CanUseSpriteAtlas"="True"}Stencil{Ref [_Stencil]Comp [_StencilComp]Pass [_StencilOp]ReadMask [_StencilReadMask]WriteMask [_StencilWriteMask]}Cull OffLighting OffZWrite OffZTest [unity_GUIZTestMode]Blend SrcAlpha OneMinusSrcAlphaColorMask [_ColorMask]Pass{Name "Default"CGPROGRAM#pragma vertex vert#pragma fragment frag#pragma target 2.0#include "UnityCG.cginc"#include "UnityUI.cginc"#pragma multi_compile_local _ UNITY_UI_CLIP_RECT#pragma multi_compile_local _ UNITY_UI_ALPHACLIPstruct appdata_t{float4 vertex   : POSITION;float4 color    : COLOR;float2 texcoord : TEXCOORD0;UNITY_VERTEX_INPUT_INSTANCE_ID};struct v2f{float4 vertex   : SV_POSITION;fixed4 color    : COLOR;float2 texcoord  : TEXCOORD0;float4 worldPosition : TEXCOORD1;UNITY_VERTEX_OUTPUT_STEREO};sampler2D _MainTex;fixed4 _Color;fixed4 _TextureSampleAdd;float4 _ClipRect;float4 _MainTex_ST;v2f vert(appdata_t v){v2f OUT;UNITY_SETUP_INSTANCE_ID(v);UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(OUT);OUT.worldPosition = v.vertex;OUT.vertex = UnityObjectToClipPos(OUT.worldPosition);OUT.texcoord = TRANSFORM_TEX(v.texcoord, _MainTex);OUT.color = v.color * _Color;return OUT;}fixed4 frag(v2f IN) : SV_Target{half4 color = (tex2D(_MainTex, IN.texcoord) + _TextureSampleAdd) * IN.color;#ifdef UNITY_UI_CLIP_RECTcolor.a *= UnityGet2DClipping(IN.worldPosition.xy, _ClipRect);#endif#ifdef UNITY_UI_ALPHACLIPclip (color.a - 0.001);#endif// 加权平均法灰度化fixed gray = dot(color.rgb, fixed3(0.299, 0.587, 0.114));color = half4(gray, gray, gray, color.a);return color;}ENDCG}}
}

5.参考文章

  • shader实验室:图片灰度化

Unity3D Shader系列之UI Image灰度化相关推荐

  1. Unity3D Shader系列之描边

    目录 1 引言 2 顶点沿法线外拓方式 2.1 法线外拓+ZTest Always 2.1.1 代码 2.1.2 问题点 2.2 法线外拓+Cull Front 2.2.1 代码 2.2.2 改进点 ...

  2. Unity3D Shader系列之全息投影

    1 效果展示 2 实现原理 全息投影其实是几个效果的叠加:①半透明效果②上下条纹的扫描效果③边缘光效果④模拟信号传输不稳定的顶点偏移效果. 咱们依次来看看这几个效果背后的原理. ①半透明效果 在Uni ...

  3. Unity3D Shader系列之渲染流水线

    目录 1 引言 2 应用阶段 2.1 加载模型数据到显存 2.2 设置渲染状态 2.3 调用Draw Call 3 几何阶段 3.1 顶点着色器 3.2 曲面细分器 3.3 几何着色器 3.4 裁剪 ...

  4. Unity3D Shader系列之圆环

    效果如下. 很简单,核心代码就下面这两句.原理就是通过计算当前uv与图片中心的距离是否在内环(_InnerRadius)和外圆(_OuterRadius)之间,若是,则将透明度置为1,若不是,则将透明 ...

  5. Unity3D Shader系列之透视效果XRay

    目录 1 效果展示 2 实现原理 2.1 渲染队列 2.2 深度测试 2.3 实现原理 3 源码 1 效果展示 如图.角色被其他物体挡住时,能够看到其基本的轮廓,就像医院的X光一样.所以这种透视效果又 ...

  6. Unity3D Shader系列之边缘光RimLight

    目录 1 引言 2 原理 3 源码 3.1 顶点-片元着色器版本 3.2 Surf着色器版本 4 参考文章 1 引言 这一两个月会花一点时间来集中总结下用Shder实现的效果.其实我一直在想为什么自己 ...

  7. Android-JNI开发系列《九》实战-Bitmap处理实现底片灰度化黑白化暖冷色调等效果

    人间观察 当你喜欢一个人的时候,总是小心翼翼的,笨笨的,傻傻的,生怕做错了什么,又怕不做什么~ 到此,Android中基本的JNI基础知识以及常见的基本操作差不多就基本讲完了.我们来实践一下,本文实现 ...

  8. Unity3D Shader 入门

    什么是Shader Shader(着色器)是一段能够针对3D对象进行操作.并被GPU所执行的程序.Shader并不是一个统一的标准,不同的图形接口的Shader并不相同.OpenGL的着色语言是GLS ...

  9. 【浅墨Unity3D Shader编程】之一 夏威夷篇:游戏场景的创建 第一个Shader的书写

    本系列文章由@浅墨_毛星云 出品,转载请注明出处. 文章链接: http://blog.csdn.net/poem_qianmo/article/details/40723789 作者:毛星云(浅墨) ...

最新文章

  1. LA 2218 (半平面交) Triathlon
  2. Qt Creator管理项目
  3. 一个简单的桌面应用程序框架示例(Java SE9)
  4. yamlcpp遍历_gf-cli 命令行工具
  5. php mysql表单验证登录_使用PHP和MySql简单身份验证 1
  6. APP 自动化测试封装结构模式
  7. java 关于时间处理
  8. 互联网产品经理的工作职责
  9. stm32Cubemx USB虚拟串口
  10. CentOS 6.4 安装D-Link 525(RT5360)无线网卡驱动
  11. android 程序控制开关飞行模式,android开关飞行模式的方法
  12. 贵大和杭电计算机科学与技术,【20考研】计算机考研专业课变动汇总
  13. 解决iOS 15上图标出现对号的问题
  14. 应用nslookup命令查看A记录、MX记录、CNAME记录和NS记录
  15. php json接口转化为数组 生成xml接口
  16. vue简单的数字滚动动画
  17. 显卡vbios修改显示接口
  18. Linux Zram的概念
  19. 富士胶片LTO磁带减排效益获认可;耀世星辉深度测试海外版语音社交产品“悦聊”CheerChat | 全球TMT...
  20. 平衡二叉树AVL左旋,右旋,双旋——java

热门文章

  1. Vscode个性化设置教程——颜色,字体,粒子特效,个性化动漫背景
  2. Promise.any 的作用,如何自己实现一个 Promise.any
  3. uniapp使用echarts
  4. 基于Spring Boot的物品租赁系统
  5. 数云融合丨《数字时代应用可持续性架构和验证白皮书》发布,转型关键技术看这里
  6. 几种非易失性存储器的比较
  7. 首次创建maven web项目,没有生成WebContent目录解决方案
  8. 爽,字节架构师DDD(领域驱动设计)巅峰之作,拆解业务代码真好用
  9. 多任务学习 (Multitask Learning) 汇总
  10. C语言-数组的定义和引用