Unity Shader – Mutual conversion between HSV and RGB

foreword

For color values,
RGB
Probably the color model we have the most exposure to, any color in an image is determined by
red (R)
,
Green (G)
,
blue (B)
Synthesized by these three channels, these three colors can be combined into almost any color.

However, it is not intuitive. For example, if I say an rgb value casually, can you guess what color it is? It is almost impossible, so the introduction of
HSV
,
HSL
and other color models.
HSV
compared to
RGB
is a more intuitive color model,
HSV
It is more in line with our human vision.

HSL and HSV concepts:

HSL
That is, hue, saturation, and brightness (English: Hue, Saturation, Lightness).

HSV
Hue, Saturation, Value (English: Hue, Saturation, Value), also known as HSB, where B is English: Brightness.

  • Hue (H) is the basic attribute of color, which is commonly referred to as the color name, such as red, yellow, etc.

  • Saturation (S) refers to the purity of the color, the higher the color, the purer the color, and the lower it will gradually become gray, taking the value of 0-100%.

  • Brightness (V), brightness (L), take 0-100%

HSL and HSV color space comparison:

Both are mathematically cylindrical, but

HSV
Conceptually, it can be thought of as an inverted cone of color (the black point is at the lower apex, and the white is at the center of the upper base);

HSL
Conceptually represents a double cone and sphere (white at the upper apex, black at the lower apex, the center of the largest cross-section is half gray).

Convert between HSV and RGB

The following functions are provided by foreign gods
Inigo Quilez
supply

https://www.shadertoy.com/view/MsS3Wc

HSB/HSV to RGB

<pre>
    
     
     // 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
     
     .
     
     the 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
     
     .
     
     the y
     
     )
     
     ;
     
     

     
     }
    
    

ShaderLab version:

<pre>
    
     
     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
     
     .
     
     the y
     
     )
     
     ;
     
     

     
     }
    
    

RGB to HSB/HSV

<pre>
    
     
     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
     
     .
     
     w
     
     )
     
     ,
     
     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
     
     .
     
     the y
     
     )
     
     ;
     
     
    
     
     float
     
      e
     
     =
     
      
     
     1.0e-10
     
     ;
     
     
    
     
     return
     
      
     
     vec3
     
     (
     
     abs
     
     (
     
     q
     
     .
     
     z 
     
      +
     
      
     
     (
     
     q
     
     .
     
     w
     
     -
     
      q
     
     .
     
     the y
     
     )
     
      
     
     /
     
      
     
     (
     
     6.0
     
      
     
     *
     
      d
     
      +
     
      e
     
     )
     
     )
     
     ,
     
     d
     
     /
     
      
     
     (
     
     q
     
     .
     
     x
     
      +
     
      e
     
     )
     
     ,
     
     q
     
     .
     
     x
     
     )
     
     ;
     
     

     
     }
    
    

ShaderLab version:

<pre>
    
     
     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
     
     .
     
     w
     
     )
     
     ,
     
      
     
     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
     
     .
     
     the y
     
     )
     
     ;
     
     
    
     
     float
     
      e
     
     =
     
      
     
     1.0e-10
     
     ;
     
     
    
     
     return
     
      
     
     float3
     
     (
     
     abs
     
     (
     
     q
     
     .
     
     z 
     
      +
     
      
     
     (
     
     q
     
     .
     
     w
     
     -
     
      q
     
     .
     
     the y
     
     )
     
      
     
     /
     
      
     
     (
     
     6.0
     
      
     
     *
     
      d
     
      +
     
      e
     
     )
     
     )
     
     ,
     
      d
     
     /
     
      
     
     (
     
     q
     
     .
     
     x
     
      +
     
      e
     
     )
     
     ,
     
      q
     
     .
     
     x
     
     )
     
     ;
     
     

     
     }
    
    

to practice

Let’s take a look in Unity Shader

SHV in Cartesian coordinate system

From the figure below, we can clearly see
X-axis determines Hue, Y-axis determines Saturation

code show as below:

<pre>
    
     
     Shader
     
     "lcl/shader2D/HSV"
     
     

     
     {<!-- -->
     
     

    SubShader
    
     
     {<!-- -->
     
     
        Pass
        
     
     {<!-- -->
     
     
            CGPROGRAM
            
     
     // vert_img is built into UnityCG.cginc
     
     
            
     
     #
     
     pragma
     
      vertex vert_img
     
     
            
     
     #
     
     pragma
     
      fragment frag
     
     

            
     
     #include
     
     "UnityCG.cginc"
     
     

            
     
     // This function is provided by the foreign god 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
     
     .
     
     the y
     
     )
     
     ;
     
     
            
     
     }
     
     

            
     
     // ---------------------------【Fragment Shader】---------------------------
     
     
            
     
     fixed4
     
      frag
     
     (
     
     v2f_img
     
      i
     
     )
     
      
     
     :
     
      SV_Target
            
     
     {<!-- -->
     
     
                
     
     fixed4
     
      col
     
     ;
     
     
                
     
     // convert hsb to rgb
     
     
                
     
     // uv.x determines hue,
     
     
                
     
     // uv.y determines brightness,
     
     
                col
     
     .
     
     rgb
     
     =
     
      
     
     hsb2rgb
     
     (
     
     float3
     
     (
     
     i
     
     .
     
     uv
     
     .
     
     x
     
     ,
     
      
     
     1
     
     ,
     
      
     
     1
     
     -
     
     i
     
     .
     
     uv
     
     .
     
     the y
     
     )
     
     )
     
     ;
     
     
                
     
     return
     
      col
     
     ;
     
     
            
     
     }
     
     
            ENDCG
        
     
     }
     
     
    
     
     }
     
     

     
     }
    
    

SHV in polar coordinates

In polar coordinates, we can see that,
Angle determines hue, radius determines saturation, brightness is fixed

Shader code is as follows:

<pre>
    
     
     Shader
     
     "lcl/shader2D/HSVInPolarCoordinate"
     
     

     
     {<!-- -->
     
     

    SubShader
    
     
     {<!-- -->
     
     
        Pass
        
     
     {<!-- -->
     
     
            CGPROGRAM
            
     
     // vert_img is built into UnityCG.cginc
     
     
            
     
     #
     
     pragma
     
      vertex vert_img
     
     
            
     
     #
     
     pragma
     
      fragment frag
     
     

            
     
     #include
     
     "UnityCG.cginc"
     
     

            
     
     #
     
     define
     
      TWO_PI 6.28318530718
     
     

            
     
     // This function is provided by the foreign god 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
     
     .
     
     the y
     
     )
     
     ;
     
     
            
     
     }
     
     

            
     
     // ---------------------------【Fragment Shader】---------------------------
     
     
            
     
     fixed4
     
      frag
     
     (
     
     v2f_img
     
      i
     
     )
     
      
     
     :
     
      SV_Target
            
     
     {<!-- -->
     
     
                
     
     fixed4
     
      col
     
     ;
     
     
                
     
     // Convert Cartesian coordinate system to polar coordinate system
     
     
                
     
     float2
     
      center
     
     =
     
      
     
     float2
     
     (
     
     0.5
     
     ,
     
     0.5
     
     )
     
     -
     
     i
     
     .
     
     uv
     
     ;
     
     
                
     
     float
     
      the angle
     
     =
     
      
     
     atan2
     
     (
     
     center
     
     .
     
     the y
     
     ,
     
     center
     
     .
     
     x
     
     )
     
     ;
     
     
                
     
     float
     
      radius
     
     =
     
      
     
     length
     
     (
     
     center
     
     )
     
     *
     
     2.0
     
     ;
     
     

                
     
     // map angles from (-PI, PI) to (0,1) range
     
     
                
     
     // The angle determines the hue, the radius determines the saturation, and the brightness is fixed
     
     
                col
     
     .
     
     rgb
     
     =
     
      
     
     hsb2rgb
     
     (
     
     float3
     
     (
     
     (
     
     the angle
     
     /
     
     TWO_PI
     
     )
     
      +
     
     0.5
     
     ,
     
     radius
     
     ,
     
     1.0
     
     )
     
     )
     
     ;
     
     
                
     
     return
     
      col
     
     ;
     
     
            
     
     }
     
     
            ENDCG
        
     
     }
     
     
    
     
     }
     
     

     
     }
    
    

at last

welcome to me
GitHub
Order a star, thank you! There are some special effect demos that I usually realize in the process of learning unity shader.