版权声明:本文为博主原创文章,遵循 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);
}