Skip to content

Instantly share code, notes, and snippets.

@nothke
Last active December 14, 2020 18:48
Show Gist options
  • Select an option

  • Save nothke/7874dfd6deb62b2bb1066a5f889583cc to your computer and use it in GitHub Desktop.

Select an option

Save nothke/7874dfd6deb62b2bb1066a5f889583cc to your computer and use it in GitHub Desktop.
// Thanks to https://twitter.com/jon_barron/status/1318946131078909952
//
using UnityEngine;
public static class CurveUtils
{
// From pico8 example by @otikik
public static float BiasGain(float x, float s, float t)
{
const float E = float.Epsilon;
if (x < t)
return (t * x) / (x + (1.0f / s - 1.0f) * (t - x) + E);
else
return 1.0f + (1.0f - t) * (x - 1.0f) / (1.0f - x - (1.0f / s - 1.0f) * (t - x) + E);
}
#if UNSAFE // Microoptimization, use only in unsafe context
// From @TommyEttinger's java implementation
public static unsafe float BiasGainBranchless(float x, float s, float t)
{
const float E = float.Epsilon;
float d = t - x;
int f = *((int*)&d) >> 31;
int n = f | 1;
return ((t * n - f) * (x + f)) / (E - f + (x + (1 - s) * d) * n) - f;
}
#endif
// From Mikko Mononen: https://gist.github.com/memononen/df1728511aed9e410d871bbff0a58c83
/// <summary>
/// Schlick Bias-Gain curve wIth 2D inflection point. Get slope values from GetInflectionSlopes() before evaluating.
/// </summary>
public static float BiasGainInflection(float x, float ix, float iy, float sa, float sb)
{
const float EPS = 1e-6f;
if (x < ix)
return (iy * x) / (x + sa * (ix - x) + EPS);
else
return ((1 - iy) * (x - 1)) / ((1 - x) - sb * (ix - x) + EPS) + 1.0f;
}
/// <summary>
/// Call this outside the evaluation loop, and use the slope parameters for BiasGainInflection()
/// </summary>
/// <param name="cx">Control point x</param>
/// <param name="cy">Control point y</param>
/// <param name="ix">Inflection point x</param>
/// <param name="iy">Inflection point y</param>
/// <param name="slopea">First slope parameter</param>
/// <param name="slopeb">Second slope parameter</param>
public static void GetInflectionSlopes(
float cx, float cy, float ix, float iy,
out float slopea, out float slopeb)
{
float slope = (iy - cy) / (ix - cx); // Second control point controls slope
slopea = slope * ((ix - 0) / (iy - 0));
slopeb = slope * ((1 - ix) / (1 - iy));
}
}
public class SchlickBiasGainTest : MonoBehaviour
{
public enum Method { BiasGain, BiasGainInflection };
public Method method;
[Range(0, 1)]
public float s = 0.8f;
[Range(0, 1)]
public float t = 0.3f;
public Vector2 inflectionPoint = new Vector2(0.4f, 0.4f);
public Vector2 controlPoint = new Vector2(0.9f, 0.52f);
const int NUM_POINTS = 100;
void OnDrawGizmos()
{
Vector2 prev = Vector2.zero;
CurveUtils.GetInflectionSlopes(
controlPoint.x, controlPoint.y, inflectionPoint.x, inflectionPoint.y,
out float slopea, out float slopeb);
for (int i = 0; i < NUM_POINTS + 1; i++)
{
float x = (float)i / NUM_POINTS;
float y;
switch (method)
{
case Method.BiasGain:
y = CurveUtils.BiasGain(x, s, t);
break;
case Method.BiasGainInflection:
y = CurveUtils.BiasGainInflection(
x, inflectionPoint.x, inflectionPoint.y, slopea, slopeb);
break;
default: y = 0; break;
}
Vector2 p = new Vector2(x, y);
Gizmos.DrawLine(prev, p);
prev = p;
}
Gizmos.color = Color.yellow;
Gizmos.DrawSphere(inflectionPoint, 0.01f);
Gizmos.color = Color.red;
Gizmos.DrawSphere(controlPoint, 0.01f);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment