HSV/HSB/HSL 色相、饱和度、亮度的色彩模型

49 篇文章 0 订阅
47 篇文章 0 订阅

什么是HSV/HSB/HSL

首先理解 HSV/HSB/HSL 都是什么

我理解:

  • HSV : Hue, Saturation, Value
  • HSB : Hue, Saturation, Brightness
  • HSL : Hue, Saturation, Lightness(或是Luminance)

Hue : 色相
Saturation: 饱和度
其中的Value、Brightness、Lightness都是用于控制亮度用的

三者同一个意思,就是叫法不一
下面使用HSV的方式来介绍

看一张图:
在这里插入图片描述

下面是我看到这张图,然后对HSV的理解

Hue 色相

从上图可以看出:Hue就是颜色的周期

逆时针一圈可以看出过渡规律:
R~RG~G~GB~B~RB~R
后面~R可以可以省略,因为可以倒回开头的R
如下:
R~RG~G~GB~B~RB~R

R~RG~G~GB~B~RB
(红~黄~绿~浅蓝~蓝~粉紫(倒回~红))

而这些过渡,对应的是:Hue的0~360的6个区间,如下伪代码,对应的是:Hue 与 RGB 的映射

// jave.lin 2019.09.03
// Hue to RGB
oneRegion = 360f/6; // 或是1f/6
region1 = onRegion;
region2 = onRegion * 2;
region3 = onRegion * 3;
region4 = onRegion * 4;
region5 = onRegion * 5;
region6 = onRegion * 6;
if (position >= region1 && position < region2) {
	// R~RG
} else if (position >= region2 && position < region3) {
	// RG~G
} else if (position >= region3 && position < region4) {
	// G~GB
} else if (position >= region4 && position < region5) {
	// GB~B
} else if (position >= region5 && position < region6) {
	// B~RB
}

Saturation饱和度

从上图可以看出来,就是颜色鲜艳度,就是对应白色到Hue色之间的过渡关系,Saturation越大越偏向于Hue色,否则偏向于白色

也可以理解为颜色的鲜艳度,饱和度高,越鲜艳,饱和度底,越灰白

对应伪代码就是:

// jave.lin 2019.09.03
// Saturation理解
float t = 0f~1f; // 或是0~100,用于控制饱和度强度
Color whiteColor = Color.white;
Color hueColor = ...; // 就是先从上面的hue中确定色相
Color FinalColor = Lerp(whiteColor, hueColor, t); // Lerp(a,b,t) => a*(1-t)+b*t,下面用到的Lerp都是一样的

Value 亮度

从上图可以看出,Value就是控制亮度的,Value值越小,越偏向黑色,否则偏向Hue色相应用Saturation后的颜色

伪代码:

// jave.lin 2019.09.03
// Value 理解
float t = 0f~1f; // 或是0~100,用于控制亮度强度
Color saturateColor = Lerp(whiteColor, hueColor, saturateT); // 上面Saturate的值
Color blackColor = Color.black;
Color FinalColor = Lerp(blackColor, saturateColor, t);

互转公式

除了上面我们自己总结的规律,下面是引用了别的博主的内容,参考:Unity Shader-后处理:简单的颜色调整(亮度,饱和度,对比度)

RGB to HSV 公式

在这里插入图片描述

HSV to RGB 公式

在这里插入图片描述在这里插入图片描述

你也可以自己去编写相关的逻辑。

然后我最近有在学习Unity,与UnityShader相关的内容,里面有部分shader代码是有应用这些公式的,源码如下:

RGB to HSV & HSV to RGB

这个是没有考虑分量大小直接转换的:

#if defined(_COLORCOLOR_ON)
half3 RGBtoHSV(half3 arg1)
{
    half4 K = half4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);
    half4 P = lerp(half4(arg1.bg, K.wz), half4(arg1.gb, K.xy), step(arg1.b, arg1.g));
    half4 Q = lerp(half4(P.xyw, arg1.r), half4(arg1.r, P.yzx), step(P.x, arg1.r));
    half D = Q.x - min(Q.w, Q.y);
    half E = 1e-10;
    return half3(abs(Q.z + (Q.w - Q.y) / (6.0 * D + E)), D / (Q.x + E), Q.x);
}

half3 HSVtoRGB(half3 arg1)
{
    half4 K = half4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);
    half3 P = abs(frac(arg1.xxx + K.xyz) * 6.0 - K.www);
    return arg1.z * lerp(K.xxx, saturate(P - K.xxx), arg1.y);
}
#endif

RGB to HSV ver1.0

这种是有考虑RGB分量那个比较高的分支处理。

half3 UnityMeta_RGBToHSVHelper(float offset, half dominantColor, half colorone, half colortwo)
{
    half H, S, V;
    V = dominantColor;

    if (V != 0.0)
    {
        half small = 0.0;
        if (colorone > colortwo)
            small = colortwo;
        else
            small = colorone;

        half diff = V - small;

        if (diff != 0)
        {
            S = diff / V;
            H = offset + ((colorone - colortwo)/diff);
        }
        else
        {
            S = 0;
            H = offset + (colorone - colortwo);
        }

        H /= 6.0;

        if (H < 6.0)
        {
            H += 1.0;
        }
    }
    else
    {
        S = 0;
        H = 0;
    }
    return half3(H, S, V);
}

half3 UnityMeta_RGBToHSV(half3 rgbColor)
{
    // when blue is highest valued
    if((rgbColor.b > rgbColor.g) && (rgbColor.b > rgbColor.r))
        return UnityMeta_RGBToHSVHelper(4.0, rgbColor.b, rgbColor.r, rgbColor.g);
    //when green is highest valued
    else if(rgbColor.g > rgbColor.r)
        return UnityMeta_RGBToHSVHelper(2.0, rgbColor.g, rgbColor.b, rgbColor.r);
    //when red is highest valued
    else
        return UnityMeta_RGBToHSVHelper(0.0, rgbColor.r, rgbColor.g, rgbColor.b);
}

RGB2HSV & HSV2RGB

		//================================================================
		// HSV HELPERS
		// source: http://lolengine.net/blog/2013/07/27/rgb-to-hsv-in-glsl

		float3 rgb2hsv(float3 c)
		{
			float4 K = float4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);
			float4 p = lerp(float4(c.bg, K.wz), float4(c.gb, K.xy), step(c.b, c.g));
			float4 q = lerp(float4(p.xyw, c.r), float4(c.r, p.yzx), step(p.x, c.r));

			float d = q.x - min(q.w, q.y);
			float e = 1.0e-10;
			return float3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x);
		}

		float3 hsv2rgb(float3 c)
		{
			c.g = max(c.g, 0.0); //make sure that saturation value is positive
			float4 K = float4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);
			float3 p = abs(frac(c.xxx + K.xyz) * 6.0 - K.www);
			return c.z * lerp(K.xxx, saturate(p - K.xxx), c.y);
		}

使用C# WinForm写了个Demo

下面是使用了最初我们总结的规律逻辑来写的(没使用公式)。

主要以:Graphics类、与Bitmap来实现, Graphics.DrawImage(Image image);
圈圈、线条都是直接Graphics.DrawLine, Graphics.DrawEllipse
在这里插入图片描述
在这里插入图片描述
主要核心代码:

        // t : [0~1]对应:0~360
        // 返回的颜色
        private void GetH(float t, out byte r, out byte g, out byte b)
        {
            r = 0; g = 0; b = 0;
            const float t1 = 1f / 6;
            const float t2 = 1f / 6 * 2;
            const float t3 = 1f / 6 * 3;
            const float t4 = 1f / 6 * 4;
            const float t5 = 1f / 6 * 5;
            const float t6 = 1f / 6 * 6;

            const float invOne = 6f;

            t = t < 0 ? 0 : t;
            t = t > 1 ? 1 : t;
            if (t >= 0 && t < t1)
            {
                // R ~ RG
                r = 255;
                g = (byte)(t * invOne * 255);
                b = 0;
            }
            else if (t >= t1 && t < t2)
            {
                // RG ~ G
                r = (byte)((1 - (t - t1) * invOne) * 255);
                g = 255;
                b = 0;
            }
            else if (t >= t2 && t < t3)
            {
                // G ~ GB
                r = 0;
                g = 255;
                b = (byte)((t - t2) * invOne * 255);
            }
            else if (t >= t3 && t < t4)
            {
                // GB ~ B
                r = 0;
                g = (byte)((1 - (t - t3) * invOne) * 255);
                b = 255;
            }
            else if (t >= t4 && t < t5)
            {
                // B ~ RB
                r = (byte)((t - t4) * invOne * 255);
                g = 0;
                b = 255;
            }
            else if (t >= t5 && t <= t6)
            {
                // RB ~ R
                r = 255;
                g = 0;
                b = (byte)((1 - (t - t5) * invOne) * 255);
            }
        }

        private void GetSV(
            // tr,tg,tb 目标颜色
            byte tr, byte tg, byte tb, 
            // st 饱和度插值系数
            // vt 亮度插值系数
            float st, float vt,
            // 输出的 rgb 颜色
            out byte r, out byte g, out byte b)
        {
            float stt = 1 - st;
            float vtt = 1 - vt;

            // 饱和度
            tb = Lerp(255, tb, st, stt);
            tg = Lerp(255, tg, st, stt);
            tr = Lerp(255, tr, st, stt);
            // 亮度
            b = Lerp(tb, 0, vt, vtt);
            g = Lerp(tg, 0, vt, vtt);
            r = Lerp(tr, 0, vt, vtt);
        }

Project

Test_HSV_HSB_HSL_色相_饱和度_亮度.zip

References

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值