谈拾取摄像机拍摄景物的颜色转化为指定颜色Demo心得

2019-12-02 13:51:10 浏览数 (2)

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。

本文链接:https://blog.csdn.net/CJB_King/article/details/79362900

谈拾取摄像机拍摄景物的颜色转化为指定颜色Demo心得

话说前段时间一个朋友找到我,说是让做个能够改变拍摄到汽车车身颜色的Demo,具体需求就是:

1.打开摄像机拍摄车体,点击车身能够拾取到车身的颜色;

2.将车身上颜色和拾取到的颜色相同的部位颜色改变为指定的颜色;

额..听上去优点绕口,不过对于程序员来说不是太难理解的啦,对吧!就是两点功能,拾取颜色和替换颜色而已啦!

一.拾取颜色

首先进行分析下,我们拾取颜色是经过鼠标或者手指点击需要拾取颜色的部位,然后读取点击部位的像素点进行存储,以便接下来进行颜色相似度判断,到这里拾取颜色的问题基本就简化了,拾取颜色我们一般用Texture2D里面的ReadPixels()方法,然后将颜色以Sprite.Create()的方式显示出来

拾取颜色的主要代码如下:

下面展示下拾取颜色主体功能代码:

代码语言:javascript复制
IEnumerator ScreenShot()
    {
        m_texture = new Texture2D(xCount, yCount, TextureFormat.RGB24, false);
        yield return new WaitForEndOfFrame();
        m_texture.ReadPixels(
            new Rect(
            (int)Input.mousePosition.x - (int)(xCount / 2),
            (int)Input.mousePosition.y - (int)(yCount / 2), 
            xCount, yCount),
                        0, 0);//获取鼠标点击部位的像素点

        m_texture.Apply();
        pickColorImage.sprite = Sprite.Create(m_texture, new Rect(0, 0, xCount, yCount), Vector2.zero);
        //将拾取到的颜色以Sprite的形式显示出来并保存

        orgCarColor.color = pickColorImage.sprite.texture.GetPixel(1,1);
        btnSetColorImage.sprite = whiteCircle;
        btnSetColorImage.color = pickColorImage.sprite.texture.GetPixel(1,1);
    }
    public void OnDrag(PointerEventData eventData) //鼠标点击拖动图片
    {
        Vector3 gloalMousePos;
        if (isPick && RectTransformUtility.ScreenPointToWorldPointInRectangle(this.transform as RectTransform, 
            eventData.position, eventData.pressEventCamera, out gloalMousePos))
        {
            mouse.transform.position = gloalMousePos;
            StartCoroutine(ScreenShot());
        }
    }

    public void OnPointerDown(PointerEventData eventData) //鼠标点击处设置图片的位置
    {
        Vector3 gloalMousePos;
        if (isPick && RectTransformUtility.ScreenPointToWorldPointInRectangle(this.transform as RectTransform, 
            eventData.position, eventData.pressEventCamera, out gloalMousePos))
        {
            mouse.transform.position = gloalMousePos;
            StartCoroutine(ScreenShot());
        }
    }

二.替换颜色

替换颜色牵扯到RGB和HSV转换问题,RGB和HSV之间转换有参考公式(RGB和HSV转换公式);

HSV颜色空间

HSV(hue,saturation,value)颜色空间的模型对应于圆柱坐标系中的一个圆锥形子集,圆锥的顶面对应于V=1。它包含RGB模型中的R=1,G=1,B=1三个面,所代表的颜色较亮。色彩H由绕V轴的旋转角给定。红色对应于角度0°,绿色对应于角度120°,蓝色对应于角度240°。在HSV颜色模型中,每一种颜色和它的补色相差180°。饱和度S取值从0到1,所以圆锥顶面的半径为1。HSV颜色模型所代表的颜色域是CIE色度图的一个子集,这个模型中饱和度为百分之百的颜色,其纯度一般小于百分之百。在圆锥的顶点(即原点)处,V=0,H和S无定义,代表黑色。圆锥的顶面中心处S=0,V=1,H无定义,代表白色。从该点到原点代表亮度渐暗的灰色,即具有不同灰度的灰色。对于这些点,S=0,H的值无定义。可以说,HSV模型中的V轴对应于RGB颜色空间中的主对角线。在圆锥顶面的圆周上的颜色,V=1,S=1,这种颜色是纯色。HSV模型对应于画家配色的方法。画家用改变色浓和色深的方法从某种纯色获得不同色调的颜色,在一种纯色中加入白色以改变色浓,加入黑色以改变色深,同时加入不同比例的白色,黑色即可获得各种不同的色调。

HSV颜色空间可以用一个圆锥空间模型来描述。

从 RGB 到HSV 的转换

设 (r, g, b) 分别是一个颜色的红、绿和蓝坐标,它们的值是在 0 到 1 之间的实数。设 max 等价于 r, g 和 b 中的最大者。设min 等于这些值中的最小者。要找到在 HSV 空间中的 (h, s, v) 值,这里的 h ∈ [0, 360)是角度的色相角,而 s, v ∈ [0,1] 是饱和度和亮度,计算为:

max=max(R,G,B) min=min(R,G,B) if R = max, H = (G-B)/(max-min) if G = max, H = 2 (B-R)/(max-min) if B = max, H = 4 (R-G)/(max-min) H = H * 60 if H < 0, H = H 360 V=max(R,G,B) S=(max-min)/max

h 的值通常规范化到位于 0 到 360°之间。而 h = 0 用于 max = min 的(就是灰色)时候而不是留下 h 未定义。

上面我们讲过了HSV的颜色空间,下面回到具体的问题,我们要将与拾取到的颜色相似的部位颜色更改为指定颜色,说到这里有人可能会说:“我直接遍历一下所有的颜色值和拾取到的颜色对比,相同的话就更改为指定颜色不就行了吗?”额...这是个解决思路,我已经试过这个方案,不过结果让人大跌 眼镜,有兴趣可以试试看哈,嗯;

好了,有点罗嗦了,直接上解决方案啦!

颜色转换不能用C#来写的,消耗太大,所以要用Shader来实现,Shader参见:

代码语言:javascript复制
Shader "MyShader/PointColorEffect"
{

	Properties
	{
		_MainTex("Texture", 2D) = "white" {}
		_MyColor("MyColor",COLOR)=(1,1,1,1)
		_TargetColor("TargetColor", Color) = (1,0,0)
		_Near("Near", Range(0, 0.5)) = 0.1
		_AddValue("AddValue",Range(0,1))=0.2
		_Alpha("Alpha",range(0,1))=1 
	}
	SubShader
	{
		Cull Off ZWrite Off ZTest Always
		Lighting Off
		Pass
		{
			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag
			
			#include "UnityCG.cginc"

			struct appdata
			{
				float4 vertex : POSITION;
				float2 uv : TEXCOORD0;
			};

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

        float3 RGBConvertToHSV(float3 rgb)  
        {  
            float R = rgb.x/255,G = rgb.y/255,B = rgb.z/255;  
            float3 hsv;  
            float max1=max(R,max(G,B));  
            float min1=min(R,min(G,B));  
            float del_max = max1 - min1;  
            hsv.z = max1;  
            if (del_max == 0)  
            {  
                hsv.x = 0;  
                hsv.y = 0;  
            }  
            else  
            {  
                hsv.y = del_max / max1;  
                float del_R = (((max1 - R) / 6)   (del_max / 2)) / del_max;  
                float del_G = (((max1 - G) / 6)   (del_max / 2)) / del_max;  
                float del_B = (((max1 - B) / 6)   (del_max / 2)) / del_max;  
                if (R == max1)hsv.x = del_B - del_G;  
                else if (G == max1)hsv.x = (1 / 3)   del_R - del_B;  
                else if (B == max1)hsv.x = (2 / 3)   del_G - del_R;  
                if (hsv.x < 0)hsv.x  = 1;  
                if (hsv.x > 1)hsv.x -= 1;  
            }  
           
            return hsv;  
        }  
				float3 HSVConvertToRGB(float3 hsv)  
				{  
					float R,G,B;  
					//float3 rgb;  
					if( hsv.y == 0 )  
					{  
						/*R=G=B=hsv.z;*/  
						R = hsv.z * 255;  
						G = hsv.z * 255;  
						B = hsv.z * 255;  
					}  
					else  
					{  
						float var_r, var_g, var_b;  
						float var_h = hsv.x * 6;  
						if (var_h == 6)var_h = 0;  
						int var_i = (int)var_h;//把var_h转化为整数var_i;  
						float var_1 = hsv.z*(1 - hsv.y);  
						float var_2 = hsv.z*(1 - hsv.y*(var_h - var_i));  
						float var_3 = hsv.z*(1 - hsv.y*(1 - (var_h - var_i)));  
						if (var_i == 0) { var_r = hsv.z; var_g = var_3; var_b = var_1; }  
						else if (var_i == 1) { var_r = var_2; var_g = hsv.z; var_b = var_1; }  
						else if (var_i == 2) { var_r = var_1; var_g = hsv.z; var_b = var_3; }  
						else if (var_i == 3) { var_r = var_1; var_g = var_2; var_b = hsv.z; }  
						else if (var_i == 4) { var_r = var_3; var_g = var_1; var_b = hsv.z; }  
						else { var_r = hsv.z; var_g = var_1; var_b = var_2; }  
  
						R = var_r * 255;  
						G = var_g * 255;  
						B = var_b * 255;  
					}  
     
					return float3(R,G,B);  
				}         
			fixed GetHue(fixed3 rgb) {
				fixed hue = 0;
				fixed minValue = min(rgb.r, min(rgb.g, rgb.b));
				fixed maxValue = max(rgb.r, max(rgb.g, rgb.b));
				fixed delta = maxValue - minValue;
				if (delta != 0) {
					if (maxValue == rgb.r) {
						hue = (rgb.g - rgb.b) / delta;
					}
					else if (maxValue == rgb.g) {
						hue = 2.0   (rgb.b - rgb.r) / delta;
					}
					else {
						hue = 4.0   (rgb.r - rgb.g) / delta;
					}

					hue /= 6.0;

					if (hue < 0) {
						hue  = 1.0;
					}
				}
				return hue;
			}

			v2f vert (appdata v)
			{
				v2f o;
				o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
				o.uv = v.uv;
				return o;
			}
			
			sampler2D _MainTex;
			fixed3 _TargetColor;
			fixed _Near;
			fixed3 _MyColor;
			float _AddValue;
			float _Alpha;

			fixed4 frag (v2f i) : SV_Target
			{
					fixed4 col = tex2D(_MainTex, i.uv);
					fixed4 tarCol= tex2D(_MainTex, i.uv);
				if(_Alpha==1)
				{
					
					fixed distance = GetHue(col.rgb) - GetHue(_TargetColor);
					if (distance > 0.5) {
						distance = 1.0 - distance;
					}
					else if (distance < -0.5) {
						distance = 1.0   distance;
					}
					else {
						distance = abs(distance);
					}
				
					if (distance <= _Near)
					{
						//tarCol.r = _MyColor.r;
						//tarCol.g = _MyColor.g;
						//tarCol.b = _MyColor.b;
						fixed3 curHSV=RGBConvertToHSV(col.rgb);
						curHSV.x=RGBConvertToHSV(_MyColor).x _AddValue;
						tarCol=fixed4(HSVConvertToRGB(curHSV),_Alpha);
				
					}
				}
				else if(_Alpha==0.5||_Alpha==-0.5)
				{
				fixed distance = RGBConvertToHSV(col.rgb).y - RGBConvertToHSV(_TargetColor).y;
				if (distance > 0.5) {
						distance = 1.0 - distance;
					}
					else if (distance < -0.5) {
						distance = 1.0   distance;
					}
					else {
						distance = abs(distance);
					}
						if (distance <= _Near&&_Alpha==0.5)    //黑色
						{
							fixed3 curHSV=RGBConvertToHSV(col.rgb);
							curHSV.x=RGBConvertToHSV(col.rgb).x;
							curHSV.y=RGBConvertToHSV(col.rgb).y;
							curHSV.z=_AddValue;
							tarCol=fixed4(HSVConvertToRGB(curHSV),0);
				
						}
						if (distance <= _Near&&_Alpha==-0.5)    //白色
						{
							fixed3 curHSV=RGBConvertToHSV(col.rgb);
							curHSV.x=RGBConvertToHSV(col.rgb).x;
							curHSV.y=0;
							curHSV.z=_AddValue;
							tarCol=fixed4(HSVConvertToRGB(curHSV),0);
				
						}
				}
			
				else if(_Alpha==0)
				{
					tarCol= tex2D(_MainTex, i.uv);
				}
				return	tarCol;
			}
			ENDCG
		}
	}
}

HSVToRGB和RGBToHSV直接套用公式;

接着我们用屏幕后期处理来实现颜色转换(指定Shader中的一些属性就可以了):

代码语言:javascript复制
 private void OnRenderImage(RenderTexture source, RenderTexture destination)
    {
        this.Material.SetColor("_TargetColor", this.TargetColor);
        this.Material.SetFloat("_Near", this.Near);
        this.Material.SetColor("_MyColor", this.MyColor);
        this.Material.SetFloat("_AddValue", this.AddValue);
        this.Material.SetFloat("_Alpha", this.Alpha);
        Graphics.Blit(source, destination, this.Material);

    }

三.效果图

0 人点赞