Results 1 to 9 of 9

Thread: HSL transforms

  1. #1

    HSL transforms

    Does anyone know of a good way to do HSL-based transformations? I'm looking for two specific things: Ways to palette-swap an image by sliding the Hue value around, and ways to change the saturation of the entire image on-screen (fade-to-gray).

    When I ask in graphics-related forums, people say "look at shaders." When I look at shaders, though, I find that they won't work for what I'm trying to do, since as far as I can tell, shaders are completely canned algorithms, and I need to be able to input an arbitrary value to use at runtime. (Although this may be totally inaccurate. It just looks that way.) Anyone know anything that might help?

    Mason

  2. #2

    HSL transforms

    You will need to do your own investigation on RGB<->HSL conversion and writing/using shaders. It's difficult that somebody will write and post a ready-to-use program for you. You can discuss the very same topic on the many different forums, but until you start working on it yourself - it won't help.

    Indeed, learning shaders will be a time consuming experience, so you have several options:

    1) Learn shaders, write your own to solve the problem. (pros: a lot of fresh knowledge; cons: time consuming)

    2) Use some kind of software rendering to solve your problem. (pros: slow, can't be used in real-time; cons: less time required)

    3) Hire someone to solve the problem for you (pros: quickly resolve the problem; cons: it might be expensive)

    4) Change the problem so you don't need to use per-pixel HSL-RGB transformations in real-time. (pros: no need to invest time and money; cons: may not be possible depending on your requirements)

    Yes, shaders will work for you, and no, they are not "completely canned algorithms", and yes, you can set arbitrary values at runtime.

  3. #3

    HSL transforms

    All I know is, what I'm trying to do has been around since before pixel shaders were invented, and worked just fine without slowing everything down. (And on computers that are much less powerful than we've got today.) So obviously there's another way. I just don't know what it is, and in fact it's far enough out of my area of expertise that I don't know where to start looking.

    I downloaded FX Composer--a program for building shaders--and read through the "basic quick start tutorial," and nothing in it made any sense to me. It came with a built-in library, including a RGB-to-HSL-transform shader. I tried to place that into a sample project, and it gave error messages with no explanations for them. Simply put, I'm trying to program something, and I don't know where to start, and all the "help" I've gotten from people in the past has been--to my level of knowledge, at least--supremely unhelpful gibberish.

    That's my situation. Is anyone actually able to help out?

  4. #4

    HSL transforms

    Why do you need to modify the color values using HSL? Could you try to specify the problem?

    If you need something, then I threw the following GLSL shader code together. It's tested and does seemingly work
    Code:
    vec3 HSL2RGB&#40;vec3 hsl&#41;
    &#123;
       if&#40;hsl.z == 0&#41;
          return vec3&#40;0,0,0&#41;;
       else
       &#123;
          if&#40;hsl.y == 0&#41;
             return vec3&#40;hsl.z&#41;;
          else
          &#123;
             float tmp2 = 0;
             if&#40;hsl.z<=0.5&#41;
                tmp2 = hsl.z*&#40;1.0+hsl.y&#41;;
             else
                tmp2 = hsl.z+hsl.y-hsl.z*hsl.y;
             float tmp1 = 2.0*hsl.z-tmp2;
             
             vec3 t3 = vec3&#40;hsl.x+1.0/3.0, hsl.x, hsl.x-1.0/3.0&#41;;
             vec3 clr = vec3&#40;0&#41;;
             
             for&#40;int i=0; i<3; i++&#41;
             &#123;
                if&#40;t3&#91;i&#93;<0>1.0&#41;
                   t3&#91;i&#93;-=1.0;
                
                if&#40;6.0*t3&#91;i&#93; < 1.0&#41;
                   clr&#91;i&#93; = tmp1+&#40;tmp2-tmp1&#41;*t3&#91;i&#93;*6.0;
                else if&#40;2.0*t3&#91;i&#93; < 1.0&#41;
                   clr&#91;i&#93; = tmp2;
                else if&#40;3.0*t3&#91;i&#93; < 2.0&#41;
                   clr&#91;i&#93; = &#40;tmp1+&#40;tmp2-tmp1&#41;*&#40;&#40;2.0/3.0&#41;-t3&#91;i&#93;&#41;*6.0&#41;;
                else
                   clr&#91;i&#93; = tmp1;
             &#125;
             
             return clr;
          &#125;
       &#125;
    &#125;
    
    vec3 RGB2HSL&#40;vec3 rgb&#41;
    &#123;
       float var_Min = min&#40;min&#40; rgb.x, rgb.y&#41;, rgb.z &#41;;    //Min. value of RGB
       float var_Max = max&#40;max&#40; rgb.x, rgb.y&#41;, rgb.z &#41;;    //Max. value of RGB
       float del_Max = var_Max - var_Min;             //Delta RGB value
       
       float H,S,L;
       
       L = &#40; var_Max + var_Min &#41; / 2.0;
       
       if &#40; del_Max == 0.0 &#41;                     //This is a gray, no chroma...
       &#123;
          H = 0.0;                                //HSL results = 0 ?? 1
          S = 0.0;
       &#125;
       else                                    //Chromatic data...
       &#123;
          if &#40; L < 0.5 &#41; S = del_Max / &#40; var_Max + var_Min &#41;;
          else           S = del_Max / &#40; 2.0 - var_Max - var_Min &#41;;
       
          float del_R = &#40; &#40; &#40; var_Max - rgb.x &#41; / 6.0 &#41; + &#40; del_Max / 2.0 &#41; &#41; / del_Max;
          float del_G = &#40; &#40; &#40; var_Max - rgb.y &#41; / 6.0 &#41; + &#40; del_Max / 2.0 &#41; &#41; / del_Max;
          float del_B = &#40; &#40; &#40; var_Max - rgb.z &#41; / 6.0 &#41; + &#40; del_Max / 2.0 &#41; &#41; / del_Max;
       
          if      &#40; rgb.x == var_Max &#41; H = del_B - del_G;
          else if &#40; rgb.y == var_Max &#41; H = &#40; 1.0 / 3.0 &#41; + del_R - del_B;
          else if &#40; rgb.z == var_Max &#41; H = &#40; 2.0 / 3.0 &#41; + del_G - del_R;
       
          if &#40; H <0> 1.0 &#41;  H -= 1.0;
       &#125;
       
       return vec3&#40;H,S,L&#41;;
    &#125;
    Peregrinus, expectavi pedes meos in cymbalis
    Nullus norvegicorum sole urinat

  5. #5

    HSL transforms

    I'm trying to do two very specific (and completely separate) things.

    1. Palette-swap an image via "hue-sliding". (Leave S and L the same, but add an arbitrary value to the H of every pixel, wrapping around if necessary). It would take an image and a byte value (the magnitude of the hue-slide) as an input, and perform the modification. This doesn't have to be done in real-time, (and probably doesn't even require a pixel shader), but it has to be fairly fast. If I were to write it in Pascal, it would look something like this:
    Code:
    &#123;$Q-&#125; &#123;$R-&#125;
    procedure hueSlide&#40;theImage&#58; TGraphic; const magnitude&#58; byte&#41;;
    var
       x, y&#58; word;
       dummy&#58; THslImage; //a pure handwavium class whose pixels are 
                         //represented internally as a record of 3 8-bit HSL
                         //values, instead of RGB ones
    begin
       if magnitude = 0 then
          Exit;
    
       dummy &#58;= THslImage.create&#40;theImage&#41;; //a "copy constructor" of sorts
       for y &#58;= 0 to height&#40;dummy&#41; do
          for x &#58;= 0 to width&#40;dummy&#41; do
             inc&#40;dummy.pixel&#91;x, y&#93;.H, magnitude&#41;;
          //end FOR
       //end FOR
       dummy.assignTo&#40;theImage&#41;; //converts it back
    end;
    &#123;$Q+&#125; &#123;$R+&#125;
    2. Saturation change. This is the really tricky one, as it would have to affect the entire screen and run in real-time. Probably the simplest way to explain would be to write it out in fake-code. Assume that the entire scene so far is rendered to a render target.
    Code:
    procedure saturationChange&#40;scene&#58; TImaginaryRenderTarget; const magnitude&#58; single&#41;;
    var
       x, y&#58; word;
       dummy&#58; THslImage;
    begin
       if magnitude = 1 then //magnitude can be any value from 0 to 2.  1 is
          Exit;              //100% &#40;ie. unchanged&#41;
       if &#40;magnitude <0> 2&#41; then
          raise EFatalError.Create&#40;'Bad magnitude value&#58; ' + intToStr&#40;magnitude&#41;&#41;;
    
       dummy &#58;= THslImage.create&#40;scene.image&#41;;
       for y &#58;= 0 to height&#40;dummy&#41; do
          for x &#58;= o to width&#40;dummy&#41; do
             dummy.pixel&#91;x, y&#93;.S, &#58;= lesserOf&#40;dummy.pixel&#91;x, y&#93;.S * magnitude, 255&#41;;
          //end FOR
       //end FOR
       dummy.assignTo&#40;scene.image&#41;; //converts it back
    end;
    Basically, a way to desaturate (fade to gray) or oversaturate the entire screen by an arbitrary value, which could change from one frame to the next. I've seen programs that predate the invention of the pixel shader, running under DirectX 2, do stuff like this in real-time with no perceptible slowdown. So this also doesn't really need a shader either, although that would probably be the best way to do it now that the technology's available. I just have no idea how to go about doing it.

    Mason

  6. #6

    HSL transforms

    This is some simple GLSL hue shift shader i wrote for TDC, it has some additional stuff for alpha opacity control and it uses uniforms to specify hue shift and alpha control values.

    Code:
    // This source code contains modified/translated code 
    // from Jedi Component Library, which is used under terms of MPL - Mozilla Public License.
    // Portions of this source code are based on online mathematical formulas from easyrgb website&#58;
    // http&#58;//www.easyrgb.com/math.php?MATH=M19#text19
    
    // This is a simple hue shift fragment shader with alpha color component control.
    
    uniform sampler2D Texture1;
    uniform float AddHue, AddSat, AddLit;
    uniform float ShadeMode;
    
    float Hue_2_RGB&#40;float v1, float v2, float vH &#41;
    &#123;
    
    float result;
    
       if &#40; vH <0> 1.0 &#41; vH -= 1.0;
       if &#40; &#40; 6.0 * vH &#41; < 1.0 &#41; &#123; result = &#40; v1 + &#40; v2 - v1 &#41; * 6.0 * vH &#41;; &#125;
       else if &#40; &#40; 2.0 * vH &#41; < 1.0 &#41; &#123; result = &#40; v2 &#41;; &#125;
       else if &#40; &#40; 3.0 * vH &#41; <2> 0.0 &#41;
    &#123;
    	gl_FragColor = vec4&#40;0,0,0, &#40;color.a * ShadeMode&#41; &#41;;
    &#125;
    else
    &#123;
    
        R = color.r;
        G = color.g;
        B = color.b;
    
    // properly process white color which cannot be properly converted to hsl and back &#40;ends up black&#41;
    if &#40;&#40;R == 1.0&#41; && &#40;G == 1.0&#41; && &#40;B == 1.0&#41;&#41;
    &#123;
        gl_FragColor = vec4&#40;color.r, color.g, color.b, color.a&#41;;	
    &#125;
    else
    &#123;
    
    // convert to HSL
    
        Cmax = max &#40;R, max &#40;G, B&#41;&#41;;
        Cmin = min &#40;R, min &#40;G, B&#41;&#41;;
    
    // calculate lightness
        L = &#40;Cmax + Cmin&#41; / 2.0;
    
      if &#40;Cmax == Cmin&#41; // it's grey
      &#123;
        H = 0.0; // it's actually undefined
        S = 0.0;
      &#125; 
      else
      &#123;
        D = Cmax - Cmin;
      &#125;
    
    
    // calculate Saturation
        if &#40;L < 0.5&#41; 
        &#123;
          S = D / &#40;Cmax + Cmin&#41;;
        &#125;
        else
        &#123;
          S = D / &#40;2.0 - Cmax - Cmin&#41;;
        &#125;
    
    // calculate Hue
        if &#40;R == Cmax&#41;
        &#123;
          H = &#40;G - B&#41; / D;
    	&#125;
        else
        &#123;
         if &#40;G == Cmax&#41;
         &#123; 
         	 H = 2.0 + &#40;B - R&#41; /D;
         &#125;
         else
         &#123;
           H = 4.0 + &#40;R - G&#41; / D;
         &#125;
    	&#125;
    	
        H = H / 6.0;
        
        if &#40;H < 0.0&#41;
        &#123;
          H = H + 1.0;
        &#125;
    
    // modify H/S/L values
    H += AddHue;
    S += AddSat;
    L += AddLit;
    
    // convert back to RGB
    
    float var_2, var_1;
    
    if &#40;S == 0.0&#41;
    &#123;
       R = L;
       G = L;
       B = L;
    &#125;
    else
    &#123;
       if &#40; L < 0.5 &#41;
       &#123; 
       var_2 = L * &#40; 1.0 + S &#41;;
       &#125;
       else
       &#123;
       var_2 = &#40; L + S &#41; - &#40; S * L &#41;;
       &#125;
    
       var_1 = 2.0 * L - var_2;
    
       R = Hue_2_RGB&#40; var_1, var_2, H + &#40; 1.0 / 3.0 &#41; &#41;;
       G = Hue_2_RGB&#40; var_1, var_2, H &#41;;
       B = Hue_2_RGB&#40; var_1, var_2, H - &#40; 1.0 / 3.0 &#41; &#41;;
    &#125;
    
        gl_FragColor = vec4&#40;R,G,B, color.a&#41;;
    
    &#125; // white color check
    
    &#125; // not shadow mode
    
    &#125;
    This is my game project - Top Down City:
    http://www.pascalgamedevelopment.com...y-Topic-Reboot

    My OpenAL audio wrapper with Intelligent Source Manager to use unlimited:
    http://www.pascalgamedevelopment.com...source+manager

  7. #7

    HSL transforms

    Mason, in DirectX 2 they've used to modify 256-color palette, which instantly changed what you see on the screen. If you work with RGB images, you need to modify each pixel to obtain the same effect.

    About desaturation - long time ago I've posted an example in Asphyre eXtreme, which could convert the screen to grayscale and back with variable intensity. This should be equal to desaturation in your case, although the brightness is conserved (while in HSL case the colors will get darker or brighter since L component is not perceptually uniform and not even linear).

    If you have a limited range of sprites, why not pre-rendering each sprite in different Hue and then using each sprite accordingly?

  8. #8

    HSL transforms

    Delfi: Thanks! I can almost understand that. (Which just means that, as long as it's been since I've touched the C language, it hasn't been long enough!) It shouldn't be too hard to modify for my purposes.

    Quote Originally Posted by Lifepower
    Mason, in DirectX 2 they've used to modify 256-color palette, which instantly changed what you see on the screen. If you work with RGB images, you need to modify each pixel to obtain the same effect.
    Figures. Yeah, that makes sense. It was all in 256-color graphics IIRC.

    About desaturation - long time ago I've posted an example in Asphyre eXtreme, which could convert the screen to grayscale and back with variable intensity. This should be equal to desaturation in your case, although the brightness is conserved (while in HSL case the colors will get darker or brighter since L component is not perceptually uniform and not even linear).
    GoneGray? I've seen that. It works pretty well, actually, and conserving brightness is just fine. But is there any way to modify it to also oversaturate as well as desaturate? I tried looking at the code to that and, again, couldn't make heads or tails of it. Too many DirectX calls.

    If you have a limited range of sprites, why not pre-rendering each sprite in different Hue and then using each sprite accordingly?
    Yeah, that's probably what I'll end up doing.

  9. #9

    HSL transforms

    Quote Originally Posted by masonwheeler
    GoneGray? I've seen that. It works pretty well, actually, and conserving brightness is just fine. But is there any way to modify it to also oversaturate as well as desaturate? I tried looking at the code to that and, again, couldn't make heads or tails of it. Too many DirectX calls.
    No, it's not how it work. Besides, if the image is completely gray (S = 0), there is ambiguity if you start increasing the saturation.

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •