Unity实现调色板

🍀 Unity实现调色板

Unity Color官方文档

Unity自带的调色板是如何实现的?RGB和色彩空间中的HSV有什么关系?

RGB

RGB分别对应的是Red(红)、Green(绿)、Blue(蓝),又称三原色光。通过对红、绿、蓝三个颜色通道(每个颜色通道都存放着图像中颜色元素的信息)的变化以及它们相互之间的叠加来得到各式各样的颜色的。即将这三种颜色在不同比例和强度上混合,可以得到其他各种颜色。

红、绿、蓝三个颜色通道每种色各分为256阶亮度,0时最暗,255时最亮。所以三色灰度都为0时,是最暗的黑色调;三色灰度都为255时,是最亮的白色调。

世界上任何一种颜色的“颜色空间”都可定义成一个固定的数字或变量。RGB(红、绿、蓝)只是众多颜色空间的一种。采用这种编码方法,每种颜色都可用三个变量来表示-红色绿色以及蓝色的强度。

HSB(HSV)

RGB是对机器友好的编码方式,想要知道色彩的明亮程度,有一种更友好的编码方式就是HSB和HSL。

HSB还有另一种对等的说法即HSV。

HSB分别代表以下含义:

H(Hub):色相。在0~360°的标准色轮上,色相是按位置度量的。黑色和白色无色相。

S(Saturation):饱和度。表示色彩的纯度,为0时为灰色。白、黑和其他灰色色彩都没有饱和度的。在最大饱和度时,每一色相具有最纯的色光。取值范围0~100%。

B(Brightness/Value):亮度。为0时即为黑色。

调色板的实现

首先我们需要明确每一部分都指代的是什么。当颜色改变时,paint模块会相应的获取到颜色的值并改变;hue就是HSV中的H(色相);Saturation中的横轴是HSV中的S(饱和度),纵轴是HSV中的V(亮度)。

unity有RGB和HSV之间互相转换的方法,我们可以直接使用,将模块的HSV值转换成RGB,或者是传入一个默认的RGB色值,定位调色板中的具体位置。

private Color HSVToRGB(Vector4 hsv)
{
    var color = Color.HSVToRGB(hsv.x, hsv.y, hsv.z);
    color.a = hsv.w;
    return color;
}
public void RGBToHSV(Color color)
{
    Color.RGBToHSV(color, out currentColorHSV.x, out currentColorHSV.y, out currentColorHSV.z);
    // 更新调色板的位置
}

Hue

我们需要通过hue模块中滑块的位置来转换RGB色值

currentColorHSV是一个初始化为private Vector4 currentColorHSV = new Vector4(0, 1, 1, 1);的坐标,x代表hue的位置,y代表S,z代表V,w代表透明度。

private Color GetHue(float y)
{
    var hueHSV = new Vector4(y, currentColorHSV.y, currentColorHSV.z, 1);
    return HSVToRGB(hueHSV);
}

alpha

该模块的处理方式和Hue模块类似,只需要传入alpha模块中滑块的位置即可

private Color GetAlpha(float y)
{
    var hueHSV = new Vector4(currentColorHSV.x, currentColorHSV.y, currentColorHSV.z, y);
    return HSVToRGB(hueHSV);
}

Saturation

该模块内部包含了一个能够滑动的图标,我们需要给这个图标规定能够滑动的范围limitBounds

limitBounds的值需要根据Saturation的大小来设置,由于此处的Saturation为一个边长为256的正方形,所以设置的边界值如下

然后我们需要获取滑块的位置,此处需要进行坐标的转换

public Camera m_camera;
public RectTransform content;
public Vector3 contentPoint;
public Vector4 limitBounds;
public ScrollRect.ScrollRectEvent onValueChanged;

private RectTransform rect;
private Vector3 screenPoint;
public override void OnPointerDown(PointerEventData eventData)
{
    screenPoint = m_camera.WorldToScreenPoint(transform.position);
    var world = m_camera.ScreenToWorldPoint(new Vector3(eventData.position.x, eventData.position.y, screenPoint.z));
    contentPoint = rect.InverseTransformPoint(world);
    contentPoint.x = Mathf.Max(limitBounds.x, Mathf.Min(limitBounds.y, contentPoint.x));
    contentPoint.y = Mathf.Max(limitBounds.z, Mathf.Min(limitBounds.w, contentPoint.y));
    content.anchoredPosition = contentPoint;
    onValueChanged.Invoke(contentPoint);
}
public void OnDrag(PointerEventData eventData)
{
    screenPoint = m_camera.WorldToScreenPoint(transform.position);
    var world = m_camera.ScreenToWorldPoint(new Vector3(eventData.position.x, eventData.position.y, screenPoint.z));
    contentPoint = rect.InverseTransformPoint(world);
    contentPoint.x = Mathf.Max(limitBounds.x, Mathf.Min(limitBounds.y, contentPoint.x));
    contentPoint.y = Mathf.Max(limitBounds.z, Mathf.Min(limitBounds.w, contentPoint.y));
    content.anchoredPosition = contentPoint;
    onValueChanged.Invoke(contentPoint);
}

然后在colorpick组件中,我们需要获取该点的位置,同时转换成为RGB,更新即可。

// 根据Saturation中滑块的位置,转换成为HSV
private Vector2 GetSaturationHSV(Vector2 point)
{
    var hsv = new Vector2();
    hsv.x = point.x / saturation.rectTransform.sizeDelta.x + 0.5f;
    hsv.y = point.y / saturation.rectTransform.sizeDelta.y + 0.5f;
    return hsv;
}
// 根据HSV值得到滑块的位置,相当于一个逆向操作
private Vector2 GetSaturationPoint(Vector4 hsv)
{
    var point = new Vector2();
    point.x = (hsv.y - 0.5f) * saturation.rectTransform.sizeDelta.x;
    point.y = (hsv.z - 0.5f) * saturation.rectTransform.sizeDelta.y;
    return point;
}
// 更新Saturation模块中滑块的位置
public void UpdateSaturationPoint(Vector4 hsv)
{
	var saturationPoint = GetSaturationPoint(hsv);
    scrollRectSaturation.content.anchoredPosition = saturationPoint;
}

最后,我们不要忘记加上对对滑块值改变的监听事件。

那么如果已知一个值,如何通过传入的值将整个面板的滑块进行重新定位呢?

只需要调用RGBToHSV函数即可,最后为什么要调用一遍OnHueClick的原因是该事件监听的是Hue中滑块的移动,即Hue滑块值的改变。那么这个值在传入RGB色值转换成为HSV后不一定会改变,导致Saturation模块不会进行刷新操作,所以可以直接调用一遍监听事件。

public void RGBToHSV(Color color)
{
    Color.RGBToHSV(color, out currentColorHSV.x, out currentColorHSV.y, out currentColorHSV.z);
    scrollbarHue.value = 1 - currentColorHSV.x;
    OnHueClick(scrollbarHue.value);
}

paint

只需要在HSV改变的时候将转换后的RGB给paint模块即可

private void PaintChange()
{
    paint.color = HSVToRGB(currentColorHSV);;
}
posted @ 2022-08-06 11:02  豆豆打丑小鸭  阅读(2440)  评论(0编辑  收藏  举报