Unity 取色板
1、 简介
功能分区为四部分:
在这里,介绍一下颜色空间吧:
1.1 RGB
RGB色彩模式是工业界的一种颜色标准,是通过对红(R)、绿(G)、蓝(B)三个颜色通道的变化以及它们相互之间的叠加来得到各式各样的颜色的,RGB即是代表红、绿、蓝三个通道的颜色,这个标准几乎包括了人类视力所能感知的所有颜色,是运用最广的颜色系统之一。
1.2 HSV
HSV(Hue, Saturation, Value)是根据颜色的直观特性由A. R. Smith在1978年创建的一种颜色空间, 也称六角锥体模型(Hexcone Model)。HSV颜色模型是指H、S、V三维颜色空间中的一个可见光子集,它包含某个颜色域的所有颜色。
每一种颜色都是由色相(Hue,简H),饱和度(Saturation,简S)和色明度(Value,简V)所表示的。这个模型中颜色的参数分别是:色调(H),饱和度(S),亮度(V)。
色调H参数表示色彩信息,即所处的光谱颜色的位置。该参数用一角度量来表示,取值范围为0°~360°。若从红色开始按逆时针方向计算,红色为0°,绿色为120°,蓝色为240°。它们的补色是:黄色为60°,青色为180°,紫色为300°;
饱和度S:取值范围为0.0~1.0;
亮度V:取值范围为0.0(黑色)~1.0(白色)。
1.3 Hexadecimal
十六进制颜色对照表:https://www.ysdaima.com/tools/hexbiao
转换原理,可参见:https://blog.csdn.net/f_957995490/article/details/120727626
2、功能实现
2.1 Slider
背景图自己P了一张,然后Slider的value为h,s=1,v=1,算出色板右上角的颜色:
imageSliderXYBG.material.color = Color.HSVToRGB(1 - v, 1, 1);
2.2 SliderXY
2.2.1 渐变色
以Shader的方式实现,其中有个问题,算出来的颜色值需要转为liner
Shader \"GradientXY\"{ Properties { _Color(\"Color\", Color) = (1,1,1,1) } SubShader { Tags { \"RenderType\" = \"Transparent\" \"Queue\" = \"Transparent\" \"IgnoreProjector\" = \"True\" \"RenderPipeline\" = \"UniversalPipeline\" } Pass { Name \"ForwardLit\" Tags { \"LightMode\" = \"UniversalForward\" } Blend SrcAlpha OneMinusSrcAlpha ZWrite Off Cull Off HLSLPROGRAM #pragma vertex vert #pragma fragment frag #pragma multi_compile _ _MAIN_LIGHT_SHADOWS #include \"Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl\" struct Attributes { float4 positionOS : POSITION; float2 uv : TEXCOORD0; half4 color : COLOR; }; struct Varyings { float4 positionCS : SV_POSITION; float2 uv : TEXCOORD0; half4 color : COLOR; float3 positionWS : TEXCOORD1; }; CBUFFER_START(UnityPerMaterial) half4 _Color; CBUFFER_END Varyings vert(Attributes v) { Varyings o; VertexPositionInputs vertexInput = GetVertexPositionInputs(v.positionOS.xyz); o.positionCS = vertexInput.positionCS; o.uv = v.uv; o.color = v.color * _Color; o.positionWS = vertexInput.positionWS; return o; } half4 gamma2liner(half4 gamma) { return gamma * (gamma * (gamma * 0.305306011h + 0.682171111h) + 0.012522878h); } half4 frag(Varyings i) : SV_Target { half4 colorWhite = half4(1,1,1,1); half4 colorBlack = half4(0,0,0,1); half4 color0 = lerp(colorWhite, _Color, i.uv.x); color0 = lerp(colorBlack, color0, i.uv.y); color0 = gamma2liner(color0); return color0; } ENDHLSL } }}
2.2.2 SliderXY实现
使用IPointerDownHandler, IDragHandler处理鼠标事件,UnityEvent处理回调事件
using UnityEngine;using UnityEngine.Events;using UnityEngine.EventSystems;public class SliderXY : MonoBehaviour, IPointerDownHandler, IDragHandler{ [SerializeField] private Vector2 value = Vector2.zero; private RectTransform self; private RectTransform handle; private UnityEvent callback = new UnityEvent(); private Vector4 boundary = Vector4.zero; public UnityEvent onValueChanged { get { return callback; } } public void OnDrag(PointerEventData eventData) { OnMouseMoving(eventData.position); } public void OnPointerDown(PointerEventData eventData) { OnMouseMoving(eventData.position); } private void Awake() { self = transform.GetComponent(); handle = transform.Find(\"Handle\").GetComponent(); boundary = new Vector4( self.position.x - self.rect.width * 0.5f, self.position.x + self.rect.width * 0.5f, self.position.y - self.rect.height * 0.5f, self.position.y + self.rect.height * 0.5f ); } private void OnMouseMoving(Vector2 pos) { pos.x = Mathf.Clamp(pos.x, boundary[0], boundary[1]); pos.y = Mathf.Clamp(pos.y, boundary[2], boundary[3]); handle.position = pos; CalculateValue(pos); OnValueChanged(); } private void CalculateValue(Vector2 mousePos) { value.x = mousePos.x - self.position.x + self.rect.width * 0.5f; value.y = mousePos.y - self.position.y + self.rect.height * 0.5f; value.x /= self.rect.width; value.y /= self.rect.height; } private void OnValueChanged() { callback.Invoke(value); } public void SetValue(Vector2 value) { this.value = value; Vector2 pos = value; pos.x *= self.rect.width; pos.y *= self.rect.height; pos.x = pos.x - self.rect.width *0.5f + self.position.x; pos.y = pos.y - self.rect.height *0.5f + self.position.y; handle.position = pos; } public Vector2 GetValue() { return value; }}
2.3 Hexadecimal
使用color.ToHexString()处理Color转Hex,ColorUtility.TryParseHtmlString处理Hex转Color
3、整体处理逻辑
using TMPro;using Unity.VisualScripting;using UnityEngine;using UnityEngine.UI;public class Palette : MonoBehaviour{ private Slider slider; private Image imageSliderXYBG; private SliderXY sliderXY; private TMP_InputField inputFieldHex; private Image imagePreview; private void Awake() { slider = transform.Find(\"Slider\").GetComponent(); imageSliderXYBG = transform.Find(\"SliderXY/Background\").GetComponent(); sliderXY = transform.Find(\"SliderXY\").GetComponent(); inputFieldHex = transform.Find(\"Hexadecimal/InputField (TMP)\").GetComponent(); imagePreview = transform.Find(\"Preview\").GetComponent(); } private void Start() { slider.onValueChanged.AddListener(v => { imageSliderXYBG.material.color = Color.HSVToRGB(1 - v, 1, 1); OnColorChanged(GetTargetColor()); }); sliderXY.onValueChanged.AddListener(v => { OnColorChanged(GetTargetColor(v)); }); inputFieldHex.onEndEdit.AddListener(v => { UnityEngine.ColorUtility.TryParseHtmlString(\"#\" + v, out Color color); OnColorChanged(color); }); slider.onValueChanged.Invoke(slider.value); } private Color GetTargetColor() { Vector2 value = sliderXY.GetValue(); return GetTargetColor(value); } private Color GetTargetColor(Vector2 value) { return Color.HSVToRGB(1 - slider.value, value.x, value.y); } private void OnColorChanged(Color color) { inputFieldHex.text = color.ToHexString()[..6]; imagePreview.color = color;//dosomthing }}