On my blog, many moons ago, I looked at optimised HLSL implementations of RGB-to-HSV and vice versa (particularly for XBox360; see the old page). I had a few requests and suggestions from readers, so I thought I'd put the latest results here for reference. I've also included HSL, HCY and HCL variants.

The RGB-to-HCV function is based on excellent work by Sam Hocevar and Emil Persson.

The HCY colour space is based on the work of Kuzma Shapran. It is a tractable hue/chroma/luminance space that tries to overcome some of the inherent problems with HSL and HSV. Thanks to David Schaeffer for pointing out some errors with my original code.

Most of the CIE-based colour spaces are computationally expensive (for pixel shaders, at least). I've included an implementation of the similar HCL colour space, but there are various problems with this scheme, not least of which is that RGB-to-HCL-to-RGB is not a perfect "round trip." Thanks to Andrew (K-Be) for helping me sort out some of the problems with my original implementation.

As for most pixel shaders, red/green/blue components are in the range zero to one inclusive. For consistency, hue/saturation/value/lightness/chroma/luminance are also in the unit range.

For a more detailed discussion of HSV/HSL, see Wikipedia.

Converting pure hue to RGB
  float3 HUEtoRGB(in float H)
  {
    float R = abs(H * 6 - 3) - 1;
    float G = 2 - abs(H * 6 - 2);
    float B = 2 - abs(H * 6 - 4);
    return saturate(float3(R,G,B));
  }
Converting RGB to hue/chroma/value
  float Epsilon = 1e-10;
 
  float3 RGBtoHCV(in float3 RGB)
  {
    // Based on work by Sam Hocevar and Emil Persson
    float4 P = (RGB.g < RGB.b) ? float4(RGB.bg, -1.0, 2.0/3.0) : float4(RGB.gb, 0.0, -1.0/3.0);
    float4 Q = (RGB.r < P.x) ? float4(P.xyw, RGB.r) : float4(RGB.r, P.yzx);
    float C = Q.x - min(Q.w, Q.y);
    float H = abs((Q.w - Q.y) / (6 * C + Epsilon) + Q.z);
    return float3(H, C, Q.x);
  }
Converting HSV to RGB
  float3 HSVtoRGB(in float3 HSV)
  {
    float3 RGB = HUEtoRGB(HSV.x);
    return ((RGB - 1) * HSV.y + 1) * HSV.z;
  }
Converting HSL to RGB
  float3 HSLtoRGB(in float3 HSL)
  {
    float3 RGB = HUEtoRGB(HSL.x);
    float C = (1 - abs(2 * HSL.z - 1)) * HSL.y;
    return (RGB - 0.5) * C + HSL.z;
  }
Converting HCY to RGB
  // The weights of RGB contributions to luminance.
  // Should sum to unity.
  float3 HCYwts = float3(0.299, 0.587, 0.114);
 
  float3 HCYtoRGB(in float3 HCY)
  {
    float3 RGB = HUEtoRGB(HCY.x);
    float Z = dot(RGB, HCYwts);
    if (HCY.z < Z)
    {
        HCY.y *= HCY.z / Z;
    }
    else if (Z < 1)
    {
        HCY.y *= (1 - HCY.z) / (1 - Z);
    }
    return (RGB - Z) * HCY.y + HCY.z;
  }
Converting HCL to RGB
  float HCLgamma = 3;
  float HCLy0 = 100;
  float HCLmaxL = 0.530454533953517; // == exp(HCLgamma / HCLy0) - 0.5
  float PI = 3.1415926536;
 
  float3 HCLtoRGB(in float3 HCL)
  {
    float3 RGB = 0;
    if (HCL.z != 0)
    {
      float H = HCL.x;
      float C = HCL.y;
      float L = HCL.z * HCLmaxL;
      float Q = exp((1 - C / (2 * L)) * (HCLgamma / HCLy0));
      float U = (2 * L - C) / (2 * Q - 1);
      float V = C / Q;
      float A = (H + min(frac(2 * H) / 4, frac(-2 * H) / 8)) * PI * 2;
      float T;
      H *= 6;
      if (H <= 0.999)
      {
        T = tan(A);
        RGB.r = 1;
        RGB.g = T / (1 + T);
      }
      else if (H <= 1.001)
      {
        RGB.r = 1;
        RGB.g = 1;
      }
      else if (H <= 2)
      {
        T = tan(A);
        RGB.r = (1 + T) / T;
        RGB.g = 1;
      }
      else if (H <= 3)
      {
        T = tan(A);
        RGB.g = 1;
        RGB.b = 1 + T;
      }
      else if (H <= 3.999)
      {
        T = tan(A);
        RGB.g = 1 / (1 + T);
        RGB.b = 1;
      }
      else if (H <= 4.001)
      {
        RGB.g = 0;
        RGB.b = 1;
      }
      else if (H <= 5)
      {
        T = tan(A);
        RGB.r = -1 / T;
        RGB.b = 1;
      }
      else
      {
        T = tan(A);
        RGB.r = 1;
        RGB.b = -T;
      }
      RGB = RGB * V + U;
    }
    return RGB;
  }
Converting RGB to HSV
  float3 RGBtoHSV(in float3 RGB)
  {
    float3 HCV = RGBtoHCV(RGB);
    float S = HCV.y / (HCV.z + Epsilon);
    return float3(HCV.x, S, HCV.z);
  }
Converting RGB to HSL
  float3 RGBtoHSL(in float3 RGB)
  {
    float3 HCV = RGBtoHCV(RGB);
    float L = HCV.z - HCV.y * 0.5;
    float S = HCV.y / (1 - abs(L * 2 - 1) + Epsilon);
    return float3(HCV.x, S, L);
  }
Converting RGB to HCY
  float3 RGBtoHCY(in float3 RGB)
  {
    // Corrected by David Schaeffer
    float3 HCV = RGBtoHCV(RGB);
    float Y = dot(RGB, HCYwts);
    float Z = dot(HUEtoRGB(HCV.x), HCYwts);
    if (Y < Z)
    {
      HCV.y *= Z / (Epsilon + Y);
    }
    else
    {
      HCV.y *= (1 - Z) / (Epsilon + 1 - Y);
    }
    return float3(HCV.x, HCV.y, Y);
  }
Converting RGB to HCL
  float3 RGBtoHCL(in float3 RGB)
  {
    float3 HCL;
    float H = 0;
    float U = min(RGB.r, min(RGB.g, RGB.b));
    float V = max(RGB.r, max(RGB.g, RGB.b));
    float Q = HCLgamma / HCLy0;
    HCL.y = V - U;
    if (HCL.y != 0)
    {
      H = atan2(RGB.g - RGB.b, RGB.r - RGB.g) / PI;
      Q *= U / V;
    }
    Q = exp(Q);
    HCL.x = frac(H / 2 - min(frac(H), frac(-H)) / 6);
    HCL.y *= Q;
    HCL.z = lerp(-U, V, Q) / (HCLmaxL * 2);
    return HCL;
  }

Converting RGB to HSI

// PI * 2
const float PI2 = 6.2831853;
vec3 rgb2hsi(vec3 c) {
     float rg = c.r - c.g;
     float rb = c.r - c.b;
     // Hue
     float h = acos((rg + rb) / (2.0 * sqrt(rg * rg + rb * (c.g - c.b)))) / PI2;
     if (c.b > c.g) h = 1.0 - h;

    // Intensity
     float i = (c.r + c.g + c.b) / 3.0;
     // Saturation
     float s = 1.0 - min(min(c.r, c.g), c.b) / i;
     return vec3(h, s, i);
}

Converting RGB to HCV

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

Converting RGB to HSV

vec3 rgb2hsv(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;
     vec3 hsv = vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x);
     return hsv;
}