hsluv / hsluv

Human-friendly HSL, website and math
https://www.hsluv.org/
MIT License
1.28k stars 55 forks source link

Plot shows CIELUV normalized to max chroma for current L. #28

Open Vaesper opened 8 years ago

Vaesper commented 8 years ago

The text next to the plot on husl-colors.org states "To the left you can see CIELUV", however this is not entirely accurate. It shows a normalized version of CIELUV, which is not immediately apparent.

It might be useful to (have an option to) normalize the view to max overall chroma.

boronine commented 8 years ago

You may be on to something, but I don't quite understand. Could you clarify what you mean by "normalized version of CIELUV"? "normalize the view to max overall chroma"?

Vaesper commented 8 years ago

You have 2 circles in the plot, if I am reading it correctly the outer one shows the maximum chroma for the given value of L and the inner one shows the maximum SAFE chroma for the given L (100% of the S-parameter of HUSLp). The first is kept constant (circle does not change size), but this implicitly means that the chroma scale changes as the L is changed.

The big problem with using HUSL to generate consistent color values is that one has to compensate for the change in the scale between saturation and chroma between different L-values; ie. you cannot use the S-value from HUSL or HUSLp to compare chroma between colors with different L. I think it would help in understanding this if one could "explore" the colorspace with a constant scale for the chroma.

boronine commented 8 years ago

I understand exactly what you mean now. I think it's a great idea. One downside is that low- and high-lightness colors will generate a picture too small to use as a color picker. When not used as a color picker, however, constant chroma is much better at communicating the real shape of CIELUV and its implications for using HUSL. Perhaps it should be an option, as you suggested. I'm going to give it some thought.

Vaesper commented 8 years ago

Good point. Perhaps an alternative would be a scale indicator for the chroma.

waldyrious commented 8 years ago

Maybe a minimap showing the current area covered ("zoomed-in" in the actual picker) overlaid on the maximum chroma (over all L) would work?

boronine commented 7 years ago

Stumbled upon an illustration of what HUSL looks like in 3D. It's a distorted RGB color cube!

guid-e5e3c131-c1bd-4a1e-9d9f-8a56a609b4fc-low

Source: http://www.hpc.ut.ee/dokumendid/ips_xe_2015/composerxe/Documentation/en_US/ipp/ipp_manual/GUID-AE698C04-81DB-402B-88E7-2BEED820D4DF.htm

In all the time working on HUSL it never crossed my mind that it's actually a cube.

no-identd commented 6 years ago

That link seems dead?

Tynach commented 2 years ago

Stumbled upon an illustration of what HUSL looks like in 3D. It's a distorted RGB color cube!

In all the time working on HUSL it never crossed my mind that it's actually a cube.

It's not really a cube, since L* is a non-linear operation. Here's a proper visualization of CIE L*u*v* that I made a little while back: https://www.shadertoy.com/view/3sSGzD

Edit: What I mean by 'not really a cube' is that the lines in that diagram look straight, but in reality the lines should all be curved.

boronine commented 2 years ago

@Tynach I've also seen diagrams of CIELUV with curved lines, but I could never understand why this happens. Is it because of the gamma correction function? In my math I'm omitting this function because I'm solving my equations for 1 and 0, where this function is a no-op: https://github.com/hsluv/hsluv/blob/84dcb726cbfb440ccd0fce7e38b66a2b1187a25f/math/hsluv.mac#L21-L28

Without this function the solution is a straight line function.

Tynach commented 2 years ago

@boronine, it's teechnically not because of gamma correction, per se. It's because of the L* function, which incidentally has a similar enough structure to sRGB's gamma correction (linear section at the start, which transitions into a squashed power curve) that my shader uses a struct to hold gamma correction parameters, defines the correct parameters for both L* and sRGB, and uses the same set of functions to perform perform both L* and sRGB's gamma.

Specifically, since that shader only has to convert from L*u*v* to sRGB, I use my toLinear() function to convert the L* channel to the Y channel in CIE XYZ, and then I use my toGamma() function to convert linear RGB to sRGB.

Note that my shader shows the shape of L*u*v* itself, displaying only colors that are within the sRGB gamut - that is, colors that are in the range of 0.0 through 1.0. Applying sRGB gamma correction changes how the in-gamut colors are distributed within L*u*v*, but does not change the actual shape; commenting out line 575 of my shader and clicking the 'compile' button (bottom left on the code itself; looks like a 'Play' button, but it shouldn't be confused with the 'Play' button on the shader view itself) will show what it looks like without sRGB gamma correction.

Because nonlinear operations are limited to the the L* component, for a given L* value the lines defining the gamut will be straight.


Pre-post Edit: Actually, it seems there's a second source for some of the observed curvature. I just now tried changing line 567 to be color.y /= 100.0; instead of color.y = toLinear(color.y/100.0, gamLab);, and it still curves in a few places, notably in the blues and magentas.

I believe this is because the .x component (starts out storing u*; I wanted L* to be .y, so I made .x be u* and .z be v*) gets divided by the .z component in line 568.

In fact, removing that final division indeed gets me entirely straight lines, but now the shape stretches toward magenta much further, and I have to edit the last mat4 in the movement matrix's definition to re-center it. Re-adding the L* 'gamma' (the toLinear() call on line 567) adds back some of the curved lines, and stretches the shape even further in the same direction as removing the /color.z did.

Somehow, this doesn't affect the conclusion I had before. I applied the same changes (removing /color.z, removing the L* 'gamma' function, and several combinations thereof), and always had straight lines for the boundaries on my non-3D L*u*v* shader. So, nonlinear operations are still only applied to the L* channel.

Though, the shape stayed less consistent between L* values; for example, when only eliminating the L* toLinear() function's usage, the magenta corner seemed to squeeze toward the middle as L* lowers, before it gets chopped off by going out of gamut.

boronine commented 2 years ago

Oooooh that explains it! Of course, the L* channel is the one I don't really see the shape of when I play with my color picker so I never considered that it's got curved lines.