Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

quarticOut doesn't work correctly #7

Open
patrickmatte opened this issue Jul 16, 2020 · 5 comments
Open

quarticOut doesn't work correctly #7

patrickmatte opened this issue Jul 16, 2020 · 5 comments

Comments

@patrickmatte
Copy link

The visual result looks like a weird inversion

@patrickmatte
Copy link
Author

I found the proper quartic function here https://github.com/zadvorsky/three.bas/tree/master/src/glsl.
float quarticOut(float t) {
return 1.0 - pow(1.0 - t, 4.0);
}

@rreusser
Copy link
Member

rreusser commented Jul 16, 2020

Ah, this might be a weird one that depends on undefined behavior in pow. I think it needs to be:

float quarticOut(float t) {
  return -pow(abs(t - 1.0), 3.0) * (1.0 - t) + 1.0;
}

I'd actually just break it up and write:

float quarticOut(float t) {
  float tm1 = t - 1.0;
  float tm1_2 = tm1 * tm1;
  return 1.0 - tm1_2 * tm1_2;
}

Through a weird quirk of glsl, the behavior pow(a, b) is strictly undefined when a < 0. You might get the answer you want, but there's really no guarantee. As a result, it's easy to mix up and write code that won't work on a different GPU. TBH I'd just use multiplication instead of hoping pow is properly optimized here. It's probably worth a sweep through these easings to make sure it protects against undefined behavior in pow throughout.

@rreusser
Copy link
Member

Specifically, see: https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/pow.xhtml

genType pow(genType x, genType y);
The result is undefined if x<0 or if x=0 and y≤0.

@bmschmidt bmschmidt mentioned this issue Jul 17, 2020
@bmschmidt
Copy link

Whoa, thought this was a dead library, but I just happen to have encountered this same issue this morning using the cubic in-out function. Problem seems widespread here.

Seems like in general a lot of these issues could just be avoided by using the form pow(1.0 - t, 3.0) instead of -pow(t - 1.0, 3.0), couldn't they? Then there shouldn't be problems so long as 0 <= t <= 1, which is fair to assume.

@rreusser
Copy link
Member

Correct! Though, as mentioned, I personally really try to avoid using pow(x, y) at all in favor of pow(abs(x), y) unless I definitely know that x > 0. (alternatively for integer powers, you can just multiply it out.) In this case it's pretty easy to accidentally plug t < 0 or t > 1 into an easing function, and the frustrating thing can be that the developer may test on all of their devices and not notice their error which causes completely different behavior on some other GPU.

So yes, as long as 0 <= t <= 1, you're fine. It's my opinion (I try to maintain these packages a bit but I don't claim to represent the views of the authors) that it's pretty alright not to explicitly clamp input t values to [0, 1], but that the best implementation—especially for a library which people trust to work repeatably—is one which at least doesn't fall back to behavior formally undefined in the spec, which just leads to endless debugging nightmares.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants