2011-05-04 5 views
4

2D 애니메이션을 구현하려면 베 지어 곡선으로 정의 된 변경 속도로 두 개의 키 프레임 사이에서 값을 보간합니다. 문제는 베 지어 곡선이 매개 변수 형식으로 표현되는 반면 요구 사항은 특정 시간의 값을 계산할 수 있어야한다는 것입니다.베 지어 곡선 당 간격, 보간 간의 보간법

정교하게 말하자면, 10과 40의 값이 4 초에 걸쳐 보간되고, 값이 0,0 0.2,0.3 0.5,0.5 1,1로 표현 된 베 지어 곡선에 의해 정의 된 바와 같이 일정하게 변하는 것을 가정 해 봅시다. 초당 24 프레임으로 그리는 경우 모든 프레임의 값을 평가해야합니다. 어떻게해야합니까? 나는 De Casteljau 알고리즘을 보았고 4 초 동안 24 * 4 조각으로 커브를 나누는 것이 내 문제를 해결할 것이라고 생각했지만 시간이 "x"축을 따르며 커브를 따라 가지 않는 것은 잘못된 것처럼 들린다.

곡선을 ​​평면에 그리면 x 축은 시간을 나타내고 y 축은 찾고자하는 값을 나타냅니다. 내가 실제로 필요로하는 것은 "x"에 해당하는 "y"를 찾을 수있는 것입니다. 그럼 각 프레임의 값을 24 개 사단에있는 X를 나누고 알 수

+0

도움이 되나요? http://www.gamedev.net/topic/313018-calculating-the-length-of-a-bezier-curve/? –

+0

나는 이것과 많은 다른 기사를 보았고 모두가 그들 만의 해결책을 가지고있는 것처럼 보인다. 나는이 문제가 일반적인 문제이기 때문에이 문제가 표준 해결책을 가져야한다고 생각했다. – Raks

+0

나는 CG 과정, 스플라인에 대한 많은 정보에 사용 된 책에서 몇 장을 확인했지만, 곡선의 길이를 잘 모르는 표준 방법에 대해서는 언급하지 않았다. –

답변

0

당신은 몇 가지 옵션이 있습니다

의이 곡선 함수 F (t) 가정 해 봅시다는 0에서 1 곳 F 범위 매개 변수 t을 걸립니다 (0)은 곡선의 시작이고 F (1)은 곡선의 끝입니다.

단위 시간당 일정한 변화로 t를 증가시켜 곡선을 따라 동작을 애니메이션으로 만들 수 있습니다. 그래서 t는 함수 T (시간) = 상수 * 시간에 의해 정의됩니다.

예를 들어, 프레임이 1/24 초이고 0.1 단위의 비율로 커브를 따라 이동하려는 경우 초, 각 프레임을 0.1 (t/s) * 1/24 (초/프레임) 씩 증가시킵니다.

단점은 실제 속도 나 단위 시간당 이동 거리가 일정하지 않다는 것입니다. 제어점의 위치에 따라 다릅니다.

곡선을 ​​따라 속도를 균일하게 조정하려면 단위 시간당 t의 상수 변화를 수정할 수 있습니다. 그러나 속도가 크게 달라지기를 원한다면 커브의 모양을 제어하기가 어려울 것입니다. 하나의 끝점에서의 속도를 훨씬 더 크게하려면 제어점을 멀리 이동해야합니다. 그러면 제어점이 곡선의 모양을 그 점으로 당깁니다. 이것이 문제라면 t에 대해 상수가 아닌 함수를 사용하는 것을 고려할 수 있습니다. 서로 다른 장단점을 지닌 다양한 접근 방법이 있으며 솔루션을 제안하기 위해 문제에 대한 자세한 내용을 알아야합니다. 예를 들어, 과거에는 사용자가 각 키 프레임에서 속도를 정의하고 룩업 테이블을 사용하여 시간에서 매개 변수 t로 변환하여 키 프레임 속도 간 선형 속도 변화 (복잡함)가있었습니다.

다른 일반적인 종료 : 여러 베 지어 곡선을 연결하여 애니메이션을 적용하고 커브 사이를 이동할 때 속도를 연속으로 유지하려면 제어점을 인접한 커브와 대칭이되도록 제한해야합니다. Catmull-Rom 스플라인은 일반적인 방법입니다.

3

나는 같은 문제에 직면했다. 모든 애니메이션 패키지는 베 지어 곡선을 사용하여 시간에 따라 값을 제어하는 ​​것으로 보이지만, 베 지어 곡선을 y (x) 함수로 구현하는 방법에 대한 정보는 없다. 그래서 여기에 내가 생각해 낸 것이 있습니다.

차원 공간에서의 표준 큐빅 베 지어 곡선은 네 포인트 P 0 = 의해 정의 될 수있다 (X 0, Y 0) ... P 3 = (X 3, Y). 2 P 1과 P는 그 형상에 영향을주는 핸들 동안
P 0 및 P 3은 곡선의 종점이다. 파라미터 t의 ε를 사용하여, 0, 1]의 곡선을 따라 특정 지점에 대한 X 및 Y 좌표는 다음 방정식
A)를 사용하여 결정될 수있다 X = (1-t) 3 X 0 + 3t (1-t) 2 X 1 + 3t 2 2 + t 3 X 3
B를 × (1-t)) Y = (1-t) y + 3t (1-t) 2 Y 1 + 3t 2 (1-t), Y 2 + t3 Y 3.

원하는 것은 x 좌표에서 주어진 곡선의 y 좌표를 반환하는 함수 y (x)입니다. 이렇게하려면 커브가 왼쪽에서 오른쪽으로 단조롭게 이동해야하므로 다른 y 위치에서 동일한 x 좌표를 두 번 이상 차지하지 않습니다. 이것을 보장하는 가장 쉬운 방법은 X 0 < X 3 및 X 1 2 ε을 X되도록 [X 0, 3을 X] 상기 입력 포인트를 제한하는 것이다. 즉, P 은 두 개의 핸들을 사이에두고 P 의 왼쪽에 있어야합니다.

주어진 x에 대해 y를 계산하려면 먼저 x에서 t를 결정해야합니다. 따라서 t를 y로부터 구하는 것은 방정식 B에 t를 적용하는 간단한 문제입니다.

주어진 y에 대해 t를 결정하는 두 가지 방법이 있습니다.

먼저, t에 대한 2 진 검색을 시도 할 수 있습니다. 0의 하한과 1의 상한으로 시작하여 방정식 A를 통해 t에 대해이 값에 대해 x를 계산합니다. 합리적으로 가까운 근사값을 얻을 때까지 간격을 양분하십시오. 이 작업은 정상적으로 수행되어야하지만 특히 빠르지도 매우 정확하지도 않습니다.

두 번째 접근법은 실제로 t에 대한 방정식 A를 풀기위한 것입니다. 방정식이 3 차이므로 구현하기가 약간 까다 롭습니다. 반면에 계산은 정말 빨라지고 정확한 결과를 산출합니다.

식 A는
(-x 0 3X + 1 2 -3x X + 3) t 3 + (3X -6x 0 1로 쓸 수있다 3X + 2) t 2 + (0 -3x 3X + 1)의 t + (X 0 -x) = 0.
X 0 ..x 3 대한 실제 값을 삽입 우리 t 3 + B2 + C * t의 +의 D = 0위한 삼차 방정식을 얻을 [0, 1] 내에 단 하나의 해답 만 있다는 것을 우리는 알고있다. 이제 우리는 this Stack Overflow answer에 게시 된 것과 같은 알고리즘을 사용하여이 방정식을 풀 수 있습니다.

다음은이 접근법을 보여주는 작은 C# 클래스입니다. 원하는 언어로 변환 할 수있을만큼 간단해야합니다.

using System; 

public class Point { 
    public Point(double x, double y) { 
     X = x; 
     Y = y; 
    } 
    public double X { get; private set; } 
    public double Y { get; private set; } 
} 

public class BezierCurve { 
    public BezierCurve(Point p0, Point p1, Point p2, Point p3) { 
     P0 = p0; 
     P1 = p1; 
     P2 = p2; 
     P3 = p3; 
    } 

    public Point P0 { get; private set; } 
    public Point P1 { get; private set; } 
    public Point P2 { get; private set; } 
    public Point P3 { get; private set; } 

    public double? GetY(double x) { 
     // Determine t 
     double t; 
     if (x == P0.X) { 
      // Handle corner cases explicitly to prevent rounding errors 
      t = 0; 
     } else if (x == P3.X) { 
      t = 1; 
     } else { 
      // Calculate t 
      double a = -P0.X + 3 * P1.X - 3 * P2.X + P3.X; 
      double b = 3 * P0.X - 6 * P1.X + 3 * P2.X; 
      double c = -3 * P0.X + 3 * P1.X; 
      double d = P0.X - x; 
      double? tTemp = SolveCubic(a, b, c, d); 
      if (tTemp == null) return null; 
      t = tTemp.Value; 
     } 

     // Calculate y from t 
     return Cubed(1 - t) * P0.Y 
      + 3 * t * Squared(1 - t) * P1.Y 
      + 3 * Squared(t) * (1 - t) * P2.Y 
      + Cubed(t) * P3.Y; 
    } 

    // Solves the equation ax³+bx²+cx+d = 0 for x ϵ ℝ 
    // and returns the first result in [0, 1] or null. 
    private static double? SolveCubic(double a, double b, double c, double d) { 
     if (a == 0) return SolveQuadratic(b, c, d); 
     if (d == 0) return 0; 

     b /= a; 
     c /= a; 
     d /= a; 
     double q = (3.0 * c - Squared(b))/9.0; 
     double r = (-27.0 * d + b * (9.0 * c - 2.0 * Squared(b)))/54.0; 
     double disc = Cubed(q) + Squared(r); 
     double term1 = b/3.0; 

     if (disc > 0) { 
      double s = r + Math.Sqrt(disc); 
      s = (s < 0) ? -CubicRoot(-s) : CubicRoot(s); 
      double t = r - Math.Sqrt(disc); 
      t = (t < 0) ? -CubicRoot(-t) : CubicRoot(t); 

      double result = -term1 + s + t; 
      if (result >= 0 && result <= 1) return result; 
     } else if (disc == 0) { 
      double r13 = (r < 0) ? -CubicRoot(-r) : CubicRoot(r); 

      double result = -term1 + 2.0 * r13; 
      if (result >= 0 && result <= 1) return result; 

      result = -(r13 + term1); 
      if (result >= 0 && result <= 1) return result; 
     } else { 
      q = -q; 
      double dum1 = q * q * q; 
      dum1 = Math.Acos(r/Math.Sqrt(dum1)); 
      double r13 = 2.0 * Math.Sqrt(q); 

      double result = -term1 + r13 * Math.Cos(dum1/3.0); 
      if (result >= 0 && result <= 1) return result; 

      result = -term1 + r13 * Math.Cos((dum1 + 2.0 * Math.PI)/3.0); 
      if (result >= 0 && result <= 1) return result; 

      result = -term1 + r13 * Math.Cos((dum1 + 4.0 * Math.PI)/3.0); 
      if (result >= 0 && result <= 1) return result; 
     } 

     return null; 
    } 

    // Solves the equation ax² + bx + c = 0 for x ϵ ℝ 
    // and returns the first result in [0, 1] or null. 
    private static double? SolveQuadratic(double a, double b, double c) { 
     double result = (-b + Math.Sqrt(Squared(b) - 4 * a * c))/(2 * a); 
     if (result >= 0 && result <= 1) return result; 

     result = (-b - Math.Sqrt(Squared(b) - 4 * a * c))/(2 * a); 
     if (result >= 0 && result <= 1) return result; 

     return null; 
    } 

    private static double Squared(double f) { return f * f; } 

    private static double Cubed(double f) { return f * f * f; } 

    private static double CubicRoot(double f) { return Math.Pow(f, 1.0/3.0); } 
} 
0

나는 answered a similar question here입니다. 기본적으로 제어점을 알고 있다면 f (t) 함수를 y (x) 함수로 변환 할 수 있습니다. 손으로 직접 처리하지 않으려면 Wolfram Alpha와 같은 서비스를 사용하여 수학에 도움이 될 수 있습니다.

관련 문제