visgl / deck.gl

WebGL2 powered visualization framework
https://deck.gl
MIT License
11.91k stars 2.06k forks source link

[Bug] Opacity should not be gamma corrected #8972

Open donmccurdy opened 2 weeks ago

donmccurdy commented 2 weeks ago

Description

I don't believe opacity should be gamma corrected. Gamma provides a (very rough) approximation of perceived linearity of lightness in RGB colors, but there's no perceptual basis for applying gamma to opacity. When using sRGB (source of the 2.2 value) the gamma is not applied to alpha channels. Possible unwanted effects:

I see this has been around for a while (https://github.com/visgl/deck.gl/commit/aa58f4f42e2b4af2215cab3e649e467b2ccac0ca) ... has it been an issue in practice? I guess if not, perhaps it's more trouble than it's worth to change now?

Related code:

https://github.com/visgl/deck.gl/blob/cccc99e5b5980914718b686b4c9d96c97055d998/modules/core/src/lib/layer.ts#L1069-L1071

Flavors

Expected Behavior

Opacity should be passed to the shader as-is, linear, without gamma correction.

Steps to Reproduce

n/a

Environment

n/a

Logs

No response

donmccurdy commented 2 weeks ago

// apply gamma to opacity to make it visually "linear"

On second thought ... maybe what was intended by the original commit was to blend in linear space? That would certainly be a valid goal! But Math.pow(opacity, 1 / 2.2) doesn't accomplish this... it would be necessary to convert all colors from "srgb" to "srgb-linear" (using WebGPU definitions) before compositing, and then convert the drawing buffer back to "srgb" in a post-processing pass. This requires considerable care with color management throughout a codebase.

ibgreen commented 2 weeks ago

It was a long time ago, but I recall that this change represented a big visual improvement as before this opacity was extremely non-linear and visually unpredictable, and there was fairly broad agreement that this was worthwhile.

That said all your comments are on point, it is of course not a scientific approach.

donmccurdy commented 2 weeks ago

It's very possible that I've misunderstood what this change does! I'm interpreting it as saying that if we draw two polygons, in this order...

  1. red @ 100% opacity
  2. blue @ 50% opacity

... vs. ...

  1. blue @ 100% opacity
  2. red @ 50% opacity

...we get two very different results, because 50% is converted to 73%.


It would also be reasonable to just say, "deck.gl applies a power curve to opacity, to improve ease of use." Having a toe on the curve near zero (as sRGB does) could certainly make it easier to precisely select low opacities. Ease-in or ease-in-ease-out would also work ...

Screenshot 2024-06-21 at 3 32 32 PM

Source: https://developer.mozilla.org/en-US/docs/Web/CSS/easing-function

... where the curve is mostly linear, but if it's the sRGB curve by historical accident, and no one has complained, then no need to change it. If that's accurate, I will maybe just reword the comment to clarify.

ibgreen commented 2 weeks ago

Yes... This was done a long time ago but I seem to recall things ended up very "dark" before we made this change, the effect of a certain percentage was not at all what one would have expected.

I don't think I thought through things like idempotency, i.e. how order affects the results. It definitely improved things for trivial usage but quite possibly it makes things harder in other cases.

Given how many applications may have been "visually tuned" based on the current behavior, I wouldn't change this "default" if we don't have strong reasons to, though offering an option to disable it or use more advanced corrections would of course be fine.

donmccurdy commented 2 weeks ago

Ok, sounds good to me – I will just open a PR clarifying the comment then.

felixpalmer commented 1 week ago

Thanks for surfacing this. I think it would be useful to also update the docs

chrisgervang commented 1 day ago

Very interesting. I've wondered why opacity falls off non-linearly while animating near zero.. do you think this is related? Whatever the case, the built-in curves become very clear when applying a property transition or hubble animation.. it could be nice to be able to opt-out or be given an easing function to undo it.

chrisgervang commented 1 day ago

Here's a quick render test sweeping opacity from 1 to 0 over ~3.33 seconds at 30 fps to create about 1000 steps. Qualitatively, the low end (from about 0.25 to 0) has a more dramatic change compared to the rest.

https://github.com/visgl/deck.gl/assets/2461547/03a07488-3097-495f-8c4d-c6ff4b4a7501

https://github.com/visgl/deck.gl/assets/2461547/4fa4094b-c923-452c-b3f3-0e30bd97ecc3

Compare this to a matching animation created in After Effects, and the result is more like what I'd expect to see:

https://github.com/visgl/deck.gl/assets/2461547/9d4d3913-e438-4af2-804e-1733073e8d42

ibgreen commented 12 hours ago

Maybe add an opacityAdjustment / opacityCorrection / opacityEasing prop to Layer