2
votes

I am fond of random generation - and random colors - so I decided to combine them both and made a simple 2d landscape generator. What my idea was is to, depending on how high a block is, (yes, the terrain is made of blocks) make it lighter or darker, where things nearest the top are lighter, and towards the bottom are darker. I got it working in grayscale, but as I figured out, you cannot really use a base RGB color and make it lighter, given that the ratio between RGB values, or anything of the sort, seem to be unusable. Solution? HSL. Or perhaps HSV, to be honest I still don't know the difference. I am referring to H 0-360, S & V/L = 0-100. Although... well, 360 = 0, so that is 360 values, but if you actually have 0-100, that is 101. Is it really 0-359 and 1-100 (or 0-99?), but color selection editors (currently referring to GIMP... MS paint had over 100 saturation) allow you to input such values?

Anyhow, I found a formula for HSL->RGB conversion (here & here. As far as I know, the final formulas are the same, but nonetheless I will provide the code (note that this is from the latter easyrgb.com link):

Hue_2_RGB

float Hue_2_RGB(float v1, float v2, float vH)             //Function Hue_2_RGB
{
if ( vH < 0 )
    vH += 1;

if ( vH > 1 )
    vH -= 1;

if ( ( 6 * vH ) < 1 )
    return ( v1 + ( v2 - v1 ) * 6 * vH );

if ( ( 2 * vH ) < 1 )
    return ( v2 );

if ( ( 3 * vH ) < 2 )
    return ( v1 + ( v2 - v1 ) * ( ( 2 / 3 ) - vH ) * 6 );

return ( v1 );
}

and the other piece of code:

float var_1 = 0, var_2 = 0;

        if (saturation == 0)                       //HSL from 0 to 1
        {
           red = luminosity * 255;                      //RGB results from 0 to 255
           green = luminosity * 255;
           blue = luminosity * 255;
        }
        else
        {
            if ( luminosity < 0.5 )
                var_2 = luminosity * (1 + saturation);
            else
                var_2 = (luminosity + saturation) - (saturation * luminosity);

            var_1 = 2 * luminosity - var_2;

            red = 255 * Hue_2_RGB(var_1, var_2, hue + ( 1 / 3 ) );
            green = 255 * Hue_2_RGB( var_1, var_2, hue );
            blue = 255 * Hue_2_RGB( var_1, var_2, hue - ( 1 / 3 ) );
        }

Sorry, not sure of a good way to fix the whitespace on those.

I replaced H, S, L values with my own names, hue, saturation, and luminosity. I looked it back over, but unless I am missing something I replaced it correctly. The hue_2_RGB function, though, is completely unedited, besides the parts needed for C++. (e.g. variable type). I also used to have ints for everything - R, G, B, H, S, L - then it occured to me... HSL was a floating point for the formula - or at least, it would seem it should be. So I made variable used (var_1, var_2, all the v's, R, G, B, hue, saturation, luminosity) to floats. So I don't beleive it is some sort of data loss error here. Additionally, before entering the formula, I have hue /= 360, saturation /= 100, and luminosity /= 100. Note that before that point, I have hue = 59, saturation = 100, and luminosity = 70. I believe I got the hue right as 360 to ensure 0-1, but trying /= 100 didn't fix it either.

and so, my question is, why is the formula not working? Thanks if you can help.

EDIT: if the question is not clear, please comment on it.

1
if it's random, and you use rgb, why you don't use a single channel? you got always random values also in this way...Jepessen
Not sure why you think you can't use RGB for this. Simply pick a starting RGB color (you can randomly assign all 3 if you like), then multiply them all by a scalar constant. If the constant is larger than 1, it will become brighter. If it's less than 1 it will become darker.user1118321
Not sure what you mean, Daniele. Can you try to restate that? @user1118321 Does that produce colors that would be identical to changing the luminosity alone? I feel like it would change slightly. It would get lighter, but I have less hue and saturation control, not to mention other light problems, though if nothing else works IO may have to try it. To be honest, though, I think HSL would be easy to use if the problem with the formula can be found. This formula seems to be the same where I have seen it (least four times), and it must have worked for some people. Do you know why it does not workuser1533320
It won't guarantee that you're getting the same results as only changing the luminosity, but it didn't sound like that was really important for this case. If it is, I recommend using Y'CbCr instead. See my answer here for more info.user1118321
Looked at those. Unfortunately, I will readily admit, I am completely lost at how the method you pointed me to worked. (using the chroma). As such, I decided to give your other method a try, and it produced surprisingly satisfactory results, as far as I can tell, so if all else fails, I will probably use that. Although, you seem to know color systems well. Can you, even though you may not recommend it, deduce why it does not work? The one thing neat about it is, you can adjust not only light but hue and saturation. I am not sure when saturation would be extremely useful, but I think hue and-user1533320

1 Answers

0
votes

Your premise is wrong. You can just scale the RGB color. The Color class in Java for example includes commands called .darker() and .brighter(), these use a factor of .7 but you can use anything you want.

public Color darker() {
    return new Color(Math.max((int)(getRed()  *FACTOR), 0),
                     Math.max((int)(getGreen()*FACTOR), 0),
                     Math.max((int)(getBlue() *FACTOR), 0),
                     getAlpha());
}

public Color brighter() {
    int r = getRed();
    int g = getGreen();
    int b = getBlue();
    int alpha = getAlpha();

    /* From 2D group:
     * 1. black.brighter() should return grey
     * 2. applying brighter to blue will always return blue, brighter
     * 3. non pure color (non zero rgb) will eventually return white
     */
    int i = (int)(1.0/(1.0-FACTOR));
    if ( r == 0 && g == 0 && b == 0) {
        return new Color(i, i, i, alpha);
    }
    if ( r > 0 && r < i ) r = i;
    if ( g > 0 && g < i ) g = i;
    if ( b > 0 && b < i ) b = i;

    return new Color(Math.min((int)(r/FACTOR), 255),
                     Math.min((int)(g/FACTOR), 255),
                     Math.min((int)(b/FACTOR), 255),
                     alpha);
}

In short, multiply all three colors by the same static factor and you will have the same ratio of colors. It's a lossy operation and you need to be sure to crimp the colors to stay in range (which is more lossy than the rounding error).

Frankly any conversion to RGB to HSV is just math, and changing the HSV V factor is just math and changing it back is more math. You don't need any of that. You can just do the math. Which is going to be make the max component color greater without messing up the ratio between the colors.

--

If the question is more specific and you simply want better results. There are better ways to calculate this. You rather than static scaling the lightness (L does not refer to luminosity) you can convert to a luma component. Which is basically weighted in a specific way. Color science and computing is dealing with human observers and they are more important than the actual math. To account for some of these human quirks there's a need to "fix things" to be more similar to what the average human perceives. Luma scales as follows:

Y = 0.2126 R + 0.7152 G + 0.0722 B

This similarly is reflected in the weights 30,59,11 which are wrongly thought to be good color distance weights. These weighs are the color's contribution to the human perception of brightness. For example the brightest blue is seen by humans to be pretty dark. Whereas yellow (exactly opposed to blue) is seen to be so damned bright that you can't even make it out against a white background. A number of colorspaces Y'CbCr included account for these differences in perception of lightness by scaling. Then you can change that value and it will be scaled again when you scale it back.

Resulting in a different color, which should be more akin to what humans would say is a "lighter" version of the same color. There are better and better approximations of this human system and so using better and fancier math to account for it will typically give you better and better results.

For a good overview that touches on these issues. http://www.compuphase.com/cmetric.htm