public void Init(float toeStrength, float toeLength, float shoulderStrength, float shoulderLength, float shoulderAngle, float gamma) { DirectParams srcParams = default(DirectParams); toeLength = Mathf.Pow(Mathf.Clamp01(toeLength), 2.2f); toeStrength = Mathf.Clamp01(toeStrength); shoulderAngle = Mathf.Clamp01(shoulderAngle); shoulderStrength = Mathf.Clamp(shoulderStrength, 1E-05f, 0.99999f); shoulderLength = Mathf.Max(0f, shoulderLength); gamma = Mathf.Max(1E-05f, gamma); float num = toeLength * 0.5f; float num2 = (1f - toeStrength) * num; float num3 = 1f - num2; float num4 = num + num3; float num5 = (1f - shoulderStrength) * num3; float x = num + num5; float y = num2 + num5; float num6 = RuntimeUtilities.Exp2(shoulderLength) - 1f; float w = num4 + num6; srcParams.x0 = num; srcParams.y0 = num2; srcParams.x1 = x; srcParams.y1 = y; srcParams.W = w; srcParams.gamma = gamma; srcParams.overshootX = srcParams.W * 2f * shoulderAngle * shoulderLength; srcParams.overshootY = 0.5f * shoulderAngle * shoulderLength; InitSegments(srcParams); }
private void InitSegments(DirectParams srcParams) { DirectParams directParams = srcParams; whitePoint = srcParams.W; inverseWhitePoint = 1f / srcParams.W; directParams.W = 1f; directParams.x0 /= srcParams.W; directParams.x1 /= srcParams.W; directParams.overshootX = srcParams.overshootX / srcParams.W; float num = 0f; float num2 = 0f; AsSlopeIntercept(out float m, out float b, directParams.x0, directParams.x1, directParams.y0, directParams.y1); float gamma = srcParams.gamma; Segment segment = segments[1]; segment.offsetX = 0f - b / m; segment.offsetY = 0f; segment.scaleX = 1f; segment.scaleY = 1f; segment.lnA = gamma * Mathf.Log(m); segment.B = gamma; num = EvalDerivativeLinearGamma(m, b, gamma, directParams.x0); num2 = EvalDerivativeLinearGamma(m, b, gamma, directParams.x1); directParams.y0 = Mathf.Max(1E-05f, Mathf.Pow(directParams.y0, directParams.gamma)); directParams.y1 = Mathf.Max(1E-05f, Mathf.Pow(directParams.y1, directParams.gamma)); directParams.overshootY = Mathf.Pow(1f + directParams.overshootY, directParams.gamma) - 1f; x0 = directParams.x0; x1 = directParams.x1; Segment segment2 = segments[0]; segment2.offsetX = 0f; segment2.offsetY = 0f; segment2.scaleX = 1f; segment2.scaleY = 1f; SolveAB(out float lnA, out float B, directParams.x0, directParams.y0, num); segment2.lnA = lnA; segment2.B = B; Segment segment3 = segments[2]; float x = 1f + directParams.overshootX - directParams.x1; float y = 1f + directParams.overshootY - directParams.y1; SolveAB(out float lnA2, out float B2, x, y, num2); segment3.offsetX = 1f + directParams.overshootX; segment3.offsetY = 1f + directParams.overshootY; segment3.scaleX = -1f; segment3.scaleY = -1f; segment3.lnA = lnA2; segment3.B = B2; float num3 = segments[2].Eval(1f); float num4 = 1f / num3; segments[0].offsetY *= num4; segments[0].scaleY *= num4; segments[1].offsetY *= num4; segments[1].scaleY *= num4; segments[2].offsetY *= num4; segments[2].scaleY *= num4; }
public void Init(float toeStrength, float toeLength, float shoulderStrength, float shoulderLength, float shoulderAngle, float gamma) { var dstParams = new DirectParams(); // This is not actually the display gamma. It's just a UI space to avoid having to // enter small numbers for the input. const float kPerceptualGamma = 2.2f; // Constraints { toeLength = Mathf.Pow(Mathf.Clamp01(toeLength), kPerceptualGamma); toeStrength = Mathf.Clamp01(toeStrength); shoulderAngle = Mathf.Clamp01(shoulderAngle); shoulderStrength = Mathf.Clamp(shoulderStrength, 1e-5f, 1f - 1e-5f); shoulderLength = Mathf.Max(0f, shoulderLength); gamma = Mathf.Max(1e-5f, gamma); } // Apply base params { // Toe goes from 0 to 0.5 var x0 = toeLength * 0.5f; var y0 = (1f - toeStrength) * x0; // Lerp from 0 to x0 var remainingY = 1f - y0; var initialW = x0 + remainingY; var y1_offset = (1f - shoulderStrength) * remainingY; var x1 = x0 + y1_offset; var y1 = y0 + y1_offset; // Filmic shoulder strength is in F stops var extraW = RuntimeUtilities.Exp2(shoulderLength) - 1f; var W = initialW + extraW; dstParams.x0 = x0; dstParams.y0 = y0; dstParams.x1 = x1; dstParams.y1 = y1; dstParams.W = W; // Bake the linear to gamma space conversion dstParams.gamma = gamma; } dstParams.overshootX = dstParams.W * 2f * shoulderAngle * shoulderLength; dstParams.overshootY = 0.5f * shoulderAngle * shoulderLength; InitSegments(dstParams); }
void InitSegments(DirectParams srcParams) { var paramsCopy = srcParams; whitePoint = srcParams.W; inverseWhitePoint = 1f / srcParams.W; // normalize params to 1.0 range paramsCopy.W = 1f; paramsCopy.x0 /= srcParams.W; paramsCopy.x1 /= srcParams.W; paramsCopy.overshootX = srcParams.overshootX / srcParams.W; float toeM = 0f; float shoulderM = 0f; { float m, b; AsSlopeIntercept(out m, out b, paramsCopy.x0, paramsCopy.x1, paramsCopy.y0, paramsCopy.y1); float g = srcParams.gamma; // Base function of linear section plus gamma is // y = (mx+b)^g // // which we can rewrite as // y = exp(g*ln(m) + g*ln(x+b/m)) // // and our evaluation function is (skipping the if parts): /* * float x0 = (x - offsetX) * scaleX; * y0 = exp(m_lnA + m_B*log(x0)); * return y0*scaleY + m_offsetY; */ var midSegment = segments[1]; midSegment.offsetX = -(b / m); midSegment.offsetY = 0f; midSegment.scaleX = 1f; midSegment.scaleY = 1f; midSegment.lnA = g * Mathf.Log(m); midSegment.B = g; toeM = EvalDerivativeLinearGamma(m, b, g, paramsCopy.x0); shoulderM = EvalDerivativeLinearGamma(m, b, g, paramsCopy.x1); // apply gamma to endpoints paramsCopy.y0 = Mathf.Max(1e-5f, Mathf.Pow(paramsCopy.y0, paramsCopy.gamma)); paramsCopy.y1 = Mathf.Max(1e-5f, Mathf.Pow(paramsCopy.y1, paramsCopy.gamma)); paramsCopy.overshootY = Mathf.Pow(1f + paramsCopy.overshootY, paramsCopy.gamma) - 1f; } this.x0 = paramsCopy.x0; this.x1 = paramsCopy.x1; // Toe section { var toeSegment = segments[0]; toeSegment.offsetX = 0; toeSegment.offsetY = 0f; toeSegment.scaleX = 1f; toeSegment.scaleY = 1f; float lnA, B; SolveAB(out lnA, out B, paramsCopy.x0, paramsCopy.y0, toeM); toeSegment.lnA = lnA; toeSegment.B = B; } // Shoulder section { // Use the simple version that is usually too flat var shoulderSegment = segments[2]; float x0 = (1f + paramsCopy.overshootX) - paramsCopy.x1; float y0 = (1f + paramsCopy.overshootY) - paramsCopy.y1; float lnA, B; SolveAB(out lnA, out B, x0, y0, shoulderM); shoulderSegment.offsetX = (1f + paramsCopy.overshootX); shoulderSegment.offsetY = (1f + paramsCopy.overshootY); shoulderSegment.scaleX = -1f; shoulderSegment.scaleY = -1f; shoulderSegment.lnA = lnA; shoulderSegment.B = B; } // Normalize so that we hit 1.0 at our white point. We wouldn't have do this if we // skipped the overshoot part. { // Evaluate shoulder at the end of the curve float scale = segments[2].Eval(1f); float invScale = 1f / scale; segments[0].offsetY *= invScale; segments[0].scaleY *= invScale; segments[1].offsetY *= invScale; segments[1].scaleY *= invScale; segments[2].offsetY *= invScale; segments[2].scaleY *= invScale; } }