Unity Shader 之 图片灰度图过渡到彩色图效果的简单实现

Unity Shader 之 图片灰度图过渡到彩色图效果的简单实现

 

目录

Unity Shader 之 图片灰度图过渡到彩色图效果的简单实现

一、简单介绍

二、实现原理

三、注意事项

四、效果预览

六、关键代码

附录:从RGB色转为灰度色算法 说明(简单效率上)


 

一、简单介绍

Shader Language的发展方向是设计出在便携性方面可以和C++、Java等相比的高级语言,“赋予程序员灵活而方便的编程方式”,并“尽可能的控制渲染过程”同时“利用图形硬件的并行性,提高算法效率”。

本节介绍,把一张彩图转为灰度图,然后在渐变到彩色图的效果。

 

二、实现原理

1、对于彩色转灰度,有一个很著名的心理学公式:Gray = R*0.299 + G*0.587 + B*0.114;

2、使用 lerp 插值算法,在时间的控制下实现灰度图渐变到彩色图(当然也可以倒过来)

 

三、注意事项

1、lerp 算法根据需要放在shader 里面或者外面都可

 

四、效果预览

Unity Shader 之 图片灰度图过渡到彩色图效果的简单实现_第1张图片

 

 

五、实现步骤

1、打开Unity,新建一个空工程,添加一个 Quad,如下图

Unity Shader 之 图片灰度图过渡到彩色图效果的简单实现_第2张图片

 

2、在工程中,导入一张彩色图片,新建Shader 和 材质,编写 Shader

Unity Shader 之 图片灰度图过渡到彩色图效果的简单实现_第3张图片

 

3、新建一个脚本,功能是点击图片,图片变彩色,或者彩色变为灰色

Unity Shader 之 图片灰度图过渡到彩色图效果的简单实现_第4张图片

 

4、把材质赋给 Quad,如下图

Unity Shader 之 图片灰度图过渡到彩色图效果的简单实现_第5张图片

 

5、把脚本挂载到场景中,如下图

Unity Shader 之 图片灰度图过渡到彩色图效果的简单实现_第6张图片

 

6、运行场景,点击图片变色

Unity Shader 之 图片灰度图过渡到彩色图效果的简单实现_第7张图片

 

六、关键代码

1、GrayToColor.shader

Shader "Custom/GrayToColor"
{
    Properties
    {
		// 主图
        _MainTex ("Albedo (RGB)", 2D) = "white" {}
		// 插值
        _Lerp("Color Lerp",Range(0,1.0)) = 0
    }
    SubShader
    {
		// 标签
        Tags {"Queue" = "Transparent" "IgnoreProjector" = "true" "RenderType" = "Transparent"}

       ZWrite On
		Blend SrcAlpha OneMinusSrcAlpha
		Cull Off

		LOD 100

		pass{
			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag

			#include "UnityCG.cginc"

			struct appdata{
			
				float4 vertex: POSITION;
				float2 uv:TEXCOORD0;
				fixed4 color:COLOR;

			};

			struct v2f{
			
				float2 uv:TEXCOORD0;
				float4 vertex:SV_POSITION;
				fixed4 color:COLOR;
			};

			sampler2D _MainTex;
			float4 _MainTex_ST;
			float _Lerp;

			v2f vert(appdata v){
			
				v2f o;
				o.vertex = UnityObjectToClipPos(v.vertex);
				o.uv = TRANSFORM_TEX(v.uv,_MainTex);
				o.color = v.color;

				return o;
			}


			fixed4 frag(v2f i):SV_Target{

				// 主图颜色
				fixed4 renderTex = tex2D(_MainTex,i.uv);

				// 灰度 上面的参考灰度 可以比对效果
				//fixed gray = 0.2125*renderTex.r + 0.7154*renderTex.g + 0.0721*renderTex.b;
				fixed gray = 0.30 * renderTex.r + 0.59 * renderTex.g + 0.11 * renderTex.b;
				// 灰度颜色
				fixed3 grayLerp = fixed3(gray,gray,gray);
				// 插值灰度变彩色
				fixed3 finalColor = lerp(grayLerp,renderTex.rgb,_Lerp);

				// 最终颜色
				return fixed4(finalColor, renderTex.a);
				
			}


			ENDCG
		
		}

	}
    FallBack "Diffuse"
}

 

2、ClickGrayToColor.cs

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

public class ClickGrayToColor : MonoBehaviour
{
    // Start is called before the first frame update
    void Start()
    {
        
    }

    // Update is called once per frame
    void Update()
    {
        OnClick();
    }

    bool isToColor = false;
    float lerp = 0.0f;
    bool isToColorOver = true;
    bool isClick = false;
    Material currentMat;
    void OnClick()
    {
        if (Input.GetMouseButtonDown(0) && isClick ==false)
        {

            Vector3 mousePos = Input.mousePosition;


            Ray ray = Camera.main.ScreenPointToRay(mousePos);
            RaycastHit hitInfo;
            if (Physics.Raycast(ray, out hitInfo))
            {
                isClick = true;
                isToColorOver = false;
                currentMat = hitInfo.collider.GetComponent().material;
            }
        }

        if (isClick && isToColorOver == false)
        {
            if (isToColor == false)
            {
                lerp = Mathf.Lerp(lerp, 1, Time.deltaTime*2);
                if (lerp >= 0.99f)
                {
                    lerp = 1;
                    isClick = false;
                    isToColorOver = true;
                    isToColor = true;
                }


            }
            else
            {
                lerp = Mathf.Lerp(lerp, 0, Time.deltaTime*2);
                if (lerp <= 0.01f)
                {
                    lerp = 0;
                    isClick = false;
                    isToColorOver = true;
                    isToColor = false;
                }

            }

            currentMat.SetFloat("_Lerp", lerp);
        }
    }
}

 

 

附录:从RGB色转为灰度色算法 说明(简单效率上)

一、基础 
  对于彩色转灰度,有一个很著名的心理学公式:

                          Gray = R*0.299 + G*0.587 + B*0.114

二、整数算法

  而实际应用时,希望避免低速的浮点运算,所以需要整数算法。
  注意到系数都是3位精度的没有,我们可以将它们缩放1000倍来实现整数运算算法:

                          Gray = (R*299 + G*587 + B*114 + 500) / 1000

  RGB一般是8位精度,现在缩放1000倍,所以上面的运算是32位整型的运算。注意后面那个除法是整数除法,所以需要加上500来实现四舍五入。
  就是由于该算法需要32位运算,所以该公式的另一个变种很流行:

                          Gray = (R*30 + G*59 + B*11 + 50) / 100

  但是,虽说上一个公式是32位整数运算,但是根据80x86体系的整数乘除指令的特点,是可以用16位整数乘除指令来运算的。而且现在32位早普及了(AMD64都出来了),所以推荐使用上一个公式。

三、整数移位算法

  上面的整数算法已经很快了,但是有一点仍制约速度,就是最后的那个除法。移位比除法快多了,所以可以将系数缩放成 2的整数幂。
  习惯上使用16位精度,2的16次幂是65536,所以这样计算系数:

                          0.299 * 65536 = 19595.264 ≈ 19595
                          0.587 * 65536 + (0.264) = 38469.632 + 0.264 = 38469.896 ≈ 38469
                          0.114 * 65536 + (0.896) =   7471.104 + 0.896 = 7472

  可能很多人看见了,我所使用的舍入方式不是四舍五入。四舍五入会有较大的误差,应该将以前的计算结果的误差一起计算进去,舍入方式是去尾法:

  写成表达式是:

                          Gray = (R*19595 + G*38469 + B*7472) >> 16

  2至20位精度的系数:

                          Gray = (R*1 + G*2 + B*1) >> 2
                          Gray = (R*2 + G*5 + B*1) >> 3
                          Gray = (R*4 + G*10 + B*2) >> 4
                          Gray = (R*9 + G*19 + B*4) >> 5
                          Gray = (R*19 + G*37 + B*8) >> 6
                          Gray = (R*38 + G*75 + B*15) >> 7
                          Gray = (R*76 + G*150 + B*30) >> 8
                          Gray = (R*153 + G*300 + B*59) >> 9
                          Gray = (R*306 + G*601 + B*117) >> 10
                          Gray = (R*612 + G*1202 + B*234) >> 11
                          Gray = (R*1224 + G*2405 + B*467) >> 12
                          Gray = (R*2449 + G*4809 + B*934) >> 13
                          Gray = (R*4898 + G*9618 + B*1868) >> 14
                          Gray = (R*9797 + G*19235 + B*3736) >> 15
                          Gray = (R*19595 + G*38469 + B*7472) >> 16
                          Gray = (R*39190 + G*76939 + B*14943) >> 17
                          Gray = (R*78381 + G*153878 + B*29885) >> 18
                          Gray = (R*156762 + G*307757 + B*59769) >> 19
                          Gray = (R*313524 + G*615514 + B*119538) >> 20

  仔细观察上面的表格,这些精度实际上是一样的:3与4、7与8、10与11、13与14、19与20
  所以16位运算下最好的计算公式是使用7位精度,比先前那个系数缩放100倍的精度高,而且速度快:

                          Gray = (R*38 + G*75 + B*15) >> 7

  其实最有意思的还是那个2位精度的,完全可以移位优化:

                          Gray = (R + (WORD)G<<1 + B) >> 2

你可能感兴趣的:(Shader,Unity)