艺术家有时偏好使用 HSV 颜色模型而不选择 RGB 或 CMYK 模型,因为它类似于人类感觉颜色的方式。RGB 和 CMYK 分别是加法原色和减法原色模型,以原色组合的方式定义颜色,而 HSV 以人类更熟悉的方式封装了关于颜色的信息:“这是什么颜色?深浅如何?明暗如何?”
HSV模型的三维表示从RGB立方体演化而来。设想从RGB沿立方体对角线的白色顶点向黑色顶点观察,就可以看到立方体的六边形外形。六边形边界表示色彩,水平轴表示纯度,明度沿垂直轴测量。
HSV对用户来说是一种直观的颜色模型。我们可以从一种纯色彩开始,即指定色彩角H,并让V=S=1,然后我们可以通过向其中加入黑色和白色来得到我们需要的颜色。增加黑色可以减小V而S不变,同样增加白色可以减小S而V不变。例如,要得到深蓝色,V=0.4 S=1 H=240度。要得到淡蓝色,V=1 S=0.4 H=240度。 一般说来,人眼最大能区分128种不同的色彩,130种色饱和度,23种明暗度。如果我们用16Bit表示HSV的话,可以用7位存放H,4位存放S,5位存放V,即745或者655就可以满足我们的需要了。
由于HSV是一种比较直观的颜色模型,所以在许多图像编辑工具中应用比较广泛,如Photoshop(在Photoshop中叫HSB)等等,但这也决定了它不适合使用在光照模型中,许多光线混合运算、光强运算等都无法直接使用HSV来实现。
设 (r, g, b) 分别是一个颜色的红、绿和蓝坐标,它们的值是在 0 到 1 之间的实数。设 max 等价于 r, g 和 b 中的最大者。设 min 等于这些值中的最小者。
这里的 h ∈ [0, 360)是角度的色相角,而 s, v∈ [0,1] 是饱和度和亮度。
- RGB到HSV的转换
Shader "Hidden/HSV2RGB"
{
Properties
{
_MainTex("Rgb from Tex", 2D) = "" {}
_HueOffset("Hue Offset", Range(0,1)) = 1
_SaturationOffset("Hue Offset", Range(-1,0)) = 1
_ValueOffset("Hue Offset", Range(-1,0)) = 1
}
SubShader
{
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;
};
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = v.uv;
return o;
}
sampler2D _MainTex;
float _HueOffset;
float _SaturationOffset;
float _ValueOffset;
fixed4 frag (v2f i) : SV_Target
{
float4 tex = tex2D(_MainTex, i.uv);
float3 rgb = tex.rgb;
//rgb to hsv
//在HLSL中, step(a,b)既是当b>=a时返回1,否则返回0,换句话说既是当a<=b时返回1,否则返回0。因此可以把被比较数灵活的插入a或b的位置,完成小于或大于的比较。
float4 k = float4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);
float4 p = lerp(float4(rgb.bg, k.wz), float4(rgb.gb, k.xy), step(rgb.b, rgb.g));
// 比较r和max(b,g)
float4 q = lerp(float4(p.xyw, rgb.r), float4(rgb.r, p.yzx), step(p.x, rgb.r));
float d = q.x - min(q.w, q.y);
float e = 1.0e-10;
float3 hsv = float3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x);
// q.x 就是max(r,g,b)// 比较b和g
//调整明度饱和度
hsv.x = hsv.x + _HueOffset;
hsv.y = hsv.y + _SaturationOffset;
hsv.z = hsv.z + _ValueOffset;
//hsv to rgb
rgb = saturate(3.0*abs(1.0-2.0*frac(hsv.x+float3(0.0,-1.0/3.0,1.0/3.0)))-1); //明度和饱和度为1时的颜色
rgb = (lerp(float3(1,1,1),rgb,hsv.y)*hsv.z); // hsv
return fixed4(rgb, 1);
}
ENDCG
}
}
}
2.效果展示
3.分析hsv to rgb关键算法
首先将hue值转为相应的rgb颜色值,hue转rgb的公式来由可参考:【shaderforge学习笔记】 Hue节点(色相节点),接着将hue转来的rgb值与(1,1,1)以饱和度(saturation)为依据进行插值,最后乘以明度。
float saturation = 0.8; // 饱和度
float value = 0.9; //明度
float hue = 1; //色相
float3 rgb = saturate(3.0*abs(1.0-2.0*frac(hue+float3(0.0,-1.0/3.0,1.0/3.0)))-1; //明度和饱和度为1时的颜色
float3 hsv = (lerp(float3(1,1,1),rgb),saturation)*value); // hsv
PS-色相饱和度你们真的会调吗?原理知道吗?