Unity Shader - HSV 和 RGB 的相互轉換

前言

對於顏色值,RGB 可能是我們接觸最多的顏色模型,圖像中的任何顏色都是由紅色(R)綠色(G)藍色(B) 這三個通道合成的,這三種顏色可以組合成幾乎所有的顏色。

然而,它並不直觀,比如我隨便說一個rgb值,你能猜到他是什麼顏色嗎?幾乎不可能,所以,後面引入了HSVHSL等顏色模型。
HSV 相對於 RGB 來說 是一種更加直觀的顏色模型,HSV更加符合我們人類視覺。

HSL和HSV 概念:

HSL 即色相、飽和度、亮度(英語:Hue, Saturation, Lightness)。

HSV 即色相、飽和度、明度(英語:Hue, Saturation, Value),又稱HSB,其中B即英語:Brightness。

  • 色相(H)是色彩的基本屬性,就是平常所說的顏色名稱,如紅色、黃色等。
  • 飽和度(S)是指色彩的純度,越高色彩越純,低則逐漸變灰,取0-100%的數值。
  • 明度(V),亮度(L),取0-100%

HSL和HSV色彩空間比較:

二者在數學上都是圓柱,但

HSV 在概念上可以被認爲是顏色的倒圓錐體(黑點在下頂點,白色在上底面圓心);

HSL 在概念上表示了一個雙圓錐體和圓球體(白色在上頂點,黑色在下頂點,最大橫切面的圓心是半程灰色)。

image

在這裏插入圖片描述

HSV 和 RGB 之間的相互轉換

以下函數由國外大神 Iñigo Quiles 提供
https://www.shadertoy.com/view/MsS3Wc

HSB/HSV 轉 RGB

// Official HSV to RGB conversion 
vec3 hsv2rgb( in vec3 c )
{
    vec3 rgb = clamp( abs(mod(c.x*6.0+vec3(0.0,4.0,2.0),6.0)-3.0)-1.0, 0.0, 1.0 );

	return c.z * mix( vec3(1.0), rgb, c.y);
}
// Smooth HSV to RGB conversion 
// https://www.shadertoy.com/view/MsS3Wc
vec3 hsv2rgb_smooth( in vec3 c )
{
    vec3 rgb = clamp( abs(mod(c.x*6.0+vec3(0.0,4.0,2.0),6.0)-3.0)-1.0, 0.0, 1.0 );

	rgb = rgb*rgb*(3.0-2.0*rgb); // cubic smoothing	

	return c.z * mix( vec3(1.0), rgb, c.y);
}

ShaderLab版:

float3 hsb2rgb( float3 c ){
    float3 rgb = clamp( abs(fmod(c.x*6.0+float3(0.0,4.0,2.0),6)-3.0)-1.0, 0, 1);
    rgb = rgb*rgb*(3.0-2.0*rgb);
    return c.z * lerp( float3(1,1,1), rgb, c.y);
}

RGB 轉 HSB/HSV

vec3 rgb2hsb( in vec3 c ){
    vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);
    vec4 p = mix(vec4(c.bg, K.wz),vec4(c.gb, K.xy),step(c.b, c.g));
    vec4 q = mix(vec4(p.xyw, c.r),vec4(c.r, p.yzx),step(p.x, c.r));
    float d = q.x - min(q.w, q.y);
    float e = 1.0e-10;
    return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)),d / (q.x + e),q.x);
}

ShaderLab版:

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);
}

實踐

下面我們在Unity Shader 中來看看

在笛卡爾座標系下的SHV

在這裏插入圖片描述
由上圖我們可以清晰的看到

X 軸 決定 色相,Y 軸 決定 飽和度

代碼如下:

Shader "lcl/shader2D/HSV"
{
   
    SubShader
    {
        Pass
        {
            CGPROGRAM
            // vert_img 是 UnityCG.cginc 內置的
            #pragma vertex vert_img 
            #pragma fragment frag

            #include "UnityCG.cginc"

            //  該函數由國外大神 Iñigo Quiles 提供
            //  https://www.shadertoy.com/view/MsS3Wc
            float3 hsb2rgb( float3 c ){
                float3 rgb = clamp( abs(fmod(c.x*6.0+float3(0.0,4.0,2.0),6)-3.0)-1.0, 0, 1);
                rgb = rgb*rgb*(3.0-2.0*rgb);
                return c.z * lerp( float3(1,1,1), rgb, c.y);
            }
            
            // ---------------------------【片元着色器】---------------------------
            fixed4 frag (v2f_img i) : SV_Target
            {
                fixed4 col;
                // hsb 轉換爲 rgb
                // uv.x  決定 色相, 
                // uv.y  決定 亮度, 
                col.rgb = hsb2rgb(float3(i.uv.x, 1, 1-i.uv.y));
                return col;
            }
            ENDCG
        }
    }
}

在極座標系下的SHV

在這裏插入圖片描述
在極座標系下,我們可以看到,角度 決定 色相, 半徑 決定 飽和度, 亮度固定

Shader代碼如下:

Shader "lcl/shader2D/HSVInPolarCoordinate"
{
    
    SubShader
    {
        Pass
        {
            CGPROGRAM
            // vert_img 是 UnityCG.cginc 內置的
            #pragma vertex vert_img 
            #pragma fragment frag

            #include "UnityCG.cginc"

            #define TWO_PI 6.28318530718

            //  該函數由國外大神 Iñigo Quiles 提供
            //  https://www.shadertoy.com/view/MsS3Wc
            float3 hsb2rgb( float3 c ){
                float3 rgb = clamp( abs(fmod(c.x*6.0+float3(0.0,4.0,2.0),6)-3.0)-1.0, 0, 1);
                rgb = rgb*rgb*(3.0-2.0*rgb);
                return c.z * lerp( float3(1,1,1), rgb, c.y);
            }
            
            // ---------------------------【片元着色器】---------------------------
            fixed4 frag (v2f_img i) : SV_Target
            {
                fixed4 col;
                // 笛卡爾座標系轉換到極座標系
                float2 toCenter = float2(0.5,0.5)-i.uv;
                float angle = atan2(toCenter.y,toCenter.x);
                float radius = length(toCenter)*2.0;

                // 將角度 從(-PI, PI) 映射到 (0,1)範圍
                // 角度決定色相, 半徑決定飽和度, 亮度固定
                col.rgb = hsb2rgb(float3((angle/TWO_PI)+0.5,radius,1.0));
                return col;
            }
            ENDCG
        }
    }
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章