nilsberglund-orleans / YouTube-simulations

Code for simulations on YouTube
Creative Commons Zero v1.0 Universal
594 stars 39 forks source link

oklab instead of hsl for (not-yet-published) phase #1

Open Artoria2e5 opened 3 years ago

Artoria2e5 commented 3 years ago

The phase emulation at videos such as https://www.youtube.com/watch?v=4lj3gQLwa1s appears to use the lightness for magnitude and hue for phase. An issue with these videos is that the HSL color space used to define the two parameters cause variations in perceived lightness even when only hue is changed. I believe that the visual result will be improved with oklab; the rainbow gradient on the linked page is a good example of what it may look like.

Oklab will be a bit harder than hsl_to_rgb, but I don't really expect that to be a major overhead. (The formula has no ifs, for a start.) With extra human effort, all colorscheme handling can be moved to a shader.

(The standard rainbow-as-magnitude schemes can similarly use a change in color scheme. I recommend going over to https://hclwizard.org/ for a background on why HSL is getting out of use. The "HCL" (Lch[uv]) they use is not quite as good as cylindrical oklab [oklch], but it suffices to say it is much better than HSL.)

nilsberglund-orleans commented 3 years ago

Thanks for the suggestion, I will see if I manage to implement this in my code. Another commenter suggested I use HSLuv, maybe it does something similar.

Artoria2e5 commented 3 years ago

hsluv is a variant of lchuv that changes chroma from an absolute value to one relative to sRGB’s capacities. I am not a fan of that sort of colorspace (the value becomes meaningless to human perception), but it’s true that we can’t ever expect to use saturation/chroma for much anyways, and just targeting the max chroma possible would make a lot of sense.

The linked post should provide most of the math needed for oklab itself. The extra dependencies would be that of sRGB gamma and the cylindrical transform, namely:

inline float srgb_encode(float u) {
  /* The 2.2 approximation. Not far off the longer stuff. */
  return powf(u, 1 / 2.2);
}

inline float srgb_decode(float u) {
  return powf(u, 2.2);
}

#define DEGREE (M_PI / 180)
inline void ch_to_ab(float c, float h, float ab[2]){
  ab[0] = c * cosf(h * DEGREE);
  ab[1] = c * sinf(h * DEGREE);
}

Now for knowing the max chroma at a point, assume we have the code at https://bottosson.github.io/posts/gamutclipping/ and:

/* S is defined similarly to HSLuv: ratio to maximum chroma. In most cases
 * always set it to 1, since values between 0 and 1 don't mean much, and 0 is
 * just grey. */
inline void lsh_to_rgb(float L, float s, float h, float rgb[3]) {
  float fake_ab[2];
  ch_to_ab(1.0, h, fake_ab);
  LC cusp = find_cusp(fake_ab[0], fake_ab[1]);

  float c_max;
  /* Do linear approximation -- assume the slice is only a triangle. */
  if (L < cusp.L)
    c_max = L / cusp.L * cusp.C;
  else
    c_max = (L - cusp.L) / (1 - cusp.L) * cusp.C;

  float ab[2];
  ch_to_ab(c_max * s, h, ab);

  RGB out = oklab_to_linear_srgb(L, ab[0], ab[1]);
  rgb[0] = srgb_encode(out.r);
  rgb[1] = srgb_encode(out.g);
  rgb[2] = srgb_encode(out.b);
}

(All that should definitely go into a shared header.)

nilsberglund-orleans commented 3 years ago

Okay, thanks - I hope to post the code for the Schrödinger simulations real soon, and you will be very welcome to look at it. I will try to improve the color scheme in later versions.

nilsberglund-orleans commented 3 years ago

I now uploaded the Schrödinger code (as well as updated versions of the others). Feel free to have a look.

no-identd commented 2 years ago

Here's two other options:

https://github.com/adammaj1/1D-RGB-color-gradient

https://github.com/adammaj1/hsluv-color-gradient

I don't think the concerns about HSLuv you've voiced would apply to those @Artoria2e5, however, they'll only help for 1D cases (i.e. many of the other videos made), but for that specific video it wouldn't help since we have 2 parameters, not just one. I however assume there'd also exist higher dimensional than 1D perceptually uniform color map implementations for C however, but I didn't find any ad hoc

no-identd commented 2 years ago

Oklab seems like a better approach than hsluv or static color maps, but a lack of a C library doesn't make this very helpful here (which isn't to say its unhelpful!). For more 1D color maps (of somewhat limited precision), see here, particularly the New Laguna colormap: http://inversed.ru/Blog_2.htm

nilsberglund-orleans commented 2 years ago

As a first step, I have now included the option to use the color palette from HSLuv in the wave simulations. To do this, set constant COLOR_PALETTE to 1.

Artoria2e5 commented 2 years ago

lack of a C library doesn't make this very helpful her

eh, I did say "assume the functions from the blog post". These C++ stuff in the reference are pretty much C.

no-identd commented 2 years ago

@nilsberglund-orleans https://gist.github.com/mikhailov-work/6a308c20e494d9e0ccc29036b28faa7a, via https://ai.googleblog.com/2019/08/turbo-improved-rainbow-colormap-for.html

That should work.

nilsberglund-orleans commented 2 years ago

Awesome, I will try that, thanks!