최상단 광고

2012년 4월 11일 수요일

베지어 곡선 (Bezier Curve)


   매개곡선(parametric curve)으로, 수치적으로 안정된 방법은 '드 카스텔죠 (de Casteljau)' 알고리즘이다. 이를 고차원으로 일반화한 것이 베지어 곡면 (Bézier surface) 이며, 그 중 베지어 삼각형 (Bézier triangle) 이라는 것도 있다. 이 곡선은 1959년 'Paul de Casteljau'가 '드 카스텔죠' 알고리즘을 이용해 만들었으며, 1962년 이 곡선을 차체 설계에 사용한 프랑스 엔지니어 피에르 베지어 (Pierre Bézier) 에 의해 널리 알려졌다.
1.1 1차 (Linear) 베지어 곡선
1.2 2차 (Quadratic) 베지어 곡선
1.3 3차 (Cubic) 베지어 곡선
1.4 4차 베지어 곡선
2. 일반화
3. 컴퓨터 그래픽에서의 응용
4. 코드 예제
5. 유리 베지어 곡선 (Rational Bézier curves)
 
1.1 1차 (Linear) 베지어 곡선
───────────────────────────────────────────
B(t) = (1−t)P0+tP1,     t∈[0,1]
점 P0과 P1이 주어졌을 때, 단지 두 점 사이의 직선일 뿐이다.
 
1.2 2차 (Quadratic) 베지어 곡선
───────────────────────────────────────────
B(t) = (1 − t)2P0 + 2t(1 - t)P1 + t2P2,    t∈[0,1]
함수 B(t)와 점 P0P1P2를 이용하는 경로(path). 중간점 Q0은 P0에서 P1까지, Q1은 P1에서 P2까지 변하면서 1차 베지어 곡선을, 점 B(t)는 Q0에서 Q1까지 변하면서 2차 베지어 곡선을 그린다.
트루타입 글꼴은 2차 베지어 곡선으로 이루어진 베지어 스플라인을 사용한다.
 
1.3 3차 (Cubic) 베지어 곡선
───────────────────────────────────────────
 
 
B(t) = P0(1 − t)3 + 3P1t(1 - t)2 + 3P2t2(1 - t) + P3t3,    t∈[0,1]
평면 또는 3차원 공간에서 4개의 점 P0P1P2P3으로 정의되며, 방향은 P0 -> P1-> P2 -> P3이다. 보통 P1이나 P2는 방향 정보만을 위해 존재한다. P0과 P1 사이의 거리는 P3으로 돌아서기 전, P2와 곡선 간의 거리를 결정한다.
3차 베지어 곡선은 1차 베지어 곡선을 그리는 중간점 Q0Q1Q2 및 2차 베지어 곡선을 그리는 점 R0R1로 이루어진다.
PostScript, Asymptote, Metafont, GIMP (GNU Image Manipulation Program) 같은 이미징 시스템은 구부러진 모양을 그릴 때 3차 베지어 곡선으로 이루어진 베지어 스플라인을 사용한다.
 
1.4 4차 베지어 곡선
───────────────────────────────────────────
4차 베지어 곡선은 1차 베지어 곡선을 그리는 중간점 Q0Q1Q2Q3 및 2차 베지어 곡선을 그리는 R0R1R2, 그리고 3차 베지어 곡선을 그리는 S0S1로 이루어진다.
 
2. 일반화
───────────────────────────────────────────
    따라서 점 P0P1, ..., Pn이 주어졌을 때, n차 베지어 곡선식은 다음과 같다:
예를 들어 n = 5일 때:
B(t) = P0(1 - t)5 + 5P1t(1 - t)4 + 10P2t2(1 - t)3 + 10P3t3(1 - t)2 + 5P4t4(1 -t) + P5t5,     t∈[0, 1]
 
또는 BP0P1...Pn이 점 P0P1, ... , Pn에 의해 결정되는 베지어 곡선이라고 하면:
B(t) = BP0P1...Pn(t) = (1 - t)BP0P1...Pn-1(t) + tBP1P2...Pn(t)
즉, n차 베지어 곡선은 2개의 n - 1차 베지어 곡선 사이의 보간(interpolation)이다.
    또한 아래와 같은 식이 있을 때
다항식은 다음과 같으며
이는 '00 = 1'로 정의된 n차 '번스타인 기본 다항식 (Bernstein basis polynomials)'이다.
    점 Pi는 베지어 곡선의 제어점 (control point) 이다. 점 P0에서 Pn까지의 베지어 점들을 선으로 연결해서 만들어진 다각형은 '베지어 폴리곤 (또는 control polygon)'이라 하며, 이 베지어 폴리곤의 convex hull에는 베지어 곡선이 포함된다.
 
주(註)
  • 곡선은 P0에서 시작해 Pn에서 끝나는데, 이것이 이른바 '종점 보간 (endpoint interpolation)' 속성이다
  • 곡선은 모든 제어점이 곡선상에 있을 때만 직선이며, 마찬가지로 베지어 곡선은 제어점들이 동일선상에 있을 때만 직선이다
  • 곡선의 시작 (끝) 은 베지어 다각형의 처음 (마지막) 부분에 접한다
  • 곡선은 어떤 점에서도 2개 이상의 곡선으로 나눌 수 있으며, 이 때 각각의 곡선 또한 베지어 곡선이다
  • 베지어 곡선은 원처럼 단순해 보이는 곡선을 정확히 그릴 수 없다 (하지만 단위 원에서 각 내부 제어점이 외부 제어점과 수평 및 수직으로 (4/3) * (sqrt(2) - 1)만큼 떨어져 있으면, 최소 반경 오차로 4개의 베지어 곡선을 이용해 원을 근사할 수는 있다)
  • 베지어 곡선에서 일정 거리만큼 떨어진 평행한 곡선인 offset curve는 몇몇 경우를 제외하고 베지어 곡선을 이용해 정확히 만들 수 없다. 하지만 적당한 근사치를 얻을 수 있는 '경험적인 방법 (heuristic method)'은 있다.
3. 컴퓨터 그래픽에서의 응용
───────────────────────────────────────────
    컴퓨터 그래픽에서 베지어 곡선은 부드러운 곡선을 만드는데 널리 이용된다. 곡선이 제어점의 convex hull에 완전히 포함되므로, 그 점들은 그래픽으로 표시할 수 있고 곡선을 변경하는데 직관적으로 사용될 수 있다. 이동, scaling, 회전 같은 아핀변환은 곡선의 각 제어점에 원하는 변환을 적용시킴으로써 곡선에 적용시킬 수 있다.
    2차와 3차 곡선이 가장 흔히 사용되며, 이보다 차수가 높은 곡선은 풀이 비용이 크다. 보다 복잡한 모형이 필요하면 낮은 차수의 베지어 곡선들을 합치면 된다. 부드럽게 보이게 하려면, 두 곡선이 만나는 제어점과 두 곡선의 양쪽 제어점이 동일선상에 있어야 한다. 이는 흔히 Adobe Illustrator나 lnkscape 같은 프로그램에서 "path"라고 하며, 이런 다중 베지어 곡선은 SVG (Scalable Vector Graphics) 파일에서도 볼 수 있다.
    베지어 곡선을 래스터화하는 가장 단순한 방법은 간격이 좁은 점들에서 해를 구하고, 가까운 선분들을 래스터화하는 것이다. 하지만 이 방법은 점들 사이의 간격이 너무 커질 수 있기 때문에, 충분히 부드럽게 보이지 않을 수 있다. 반대로 곡선이 선형에 가까우면 너무 많은 점들이 만들어진다. 이에 대한 해결책은 반복 분할로, 곡선의 제어점들을 검사해 곡선이 선분에 가까워지는지 알아본다. 그렇지 않다면 곡선은 매개변수를 기준으로 두 부분 (0 ≤ t ≤ 0.5, 0.5 ≤ t ≤ 1) 으로 나눠지고, 나눠진 각 부분에 대해 같은 과정을 반복한다. 또다른 방법인 해석적인 방법에서는 스플라인이 각 스캔 라인과 교차하는데, 3차 다항식의 근을 구해야 하고 (3차 스플라인에 대해) 여러개의 근을 다뤄야 하므로, 실제로 자주 사용되지는 않는다.
 
4. 코드 예제
───────────────────────────────────────────
    다음 C 코드는 3차 베지어 곡선을 찍는 간단한 방법을 보여준다. 이 예제는 단순히 다항식의 계수를 계산하고 0 ~ 1 사이의 t값을 연속적으로 실행시킨다 ─ 실제로 자주 사용되는 방법은 재귀적인 방법으로, 일시적으로 더 많은 메모리가 필요하긴 하지만 더 빠르고 적은 프로세서 사이클을 소비한다. 하지만 여기에서 보이는 방법은 같은 결과를 만들어내면서도 보다 이해하기는 쉬울 것이다. 아래 코드는 연산을 명확히하기 위해 인수분해되었으며 ─ 실제로 최적화된 코드는 계수를 한번만 계산한 후 곡선 점을 계산하는 실제 루프에서 그 결과를 재사용한다 ─ 매번 계수를 다시 계산하므로 덜 효율적이지만, 이해하기는 쉬울것이다.
    곡선은 곡선 배열의 연속적인 점들 사이에 선을 그림으로써 완성된다 ─ 점이 많을수록, 곡선은 부드러워진다.
    아래 코드는 일부 아키텍처에서 동적 프로그래밍 (dynamic programming) 을 사용해 최적화할 수도 있다. 예를 들어 dt는 일정하므로, cx * t는 반복할 때마다 일정 값만큼 바뀐다. 이런 최적화를 반복 적용하면, 곱셈없는 루프를 만들 수 있다 (하지만 이런 처리는 수치적으로 안정하지는 않다).
/*** 3차 베지어 곡선을 만드는 코드 ***/
typedef struct { float x, y; } Point2D;

/***
* cp는 4개의 요소가 있는 배열로:
* cp[0]은 시작점 (P0), cp[1]은 첫번째 제어점 (P1)
* cp[2]는 두번째 제어점 (P2), cp[3]은 끝점 (P3)
* t는 매개변수로, 0 ≤ t ≤ 1
***/
Point2D PointOnCubicBezier(Point2D* cp, float t)
{
    float   ax, bx, cx, ay, by, cy, tSquared, tCubed;
    Point2D result;

    /* 다항식 계수를 계산한다 */
    cx = 3.0 * (cp[1].x - cp[0].x);
    bx = 3.0 * (cp[2].x - cp[1].x) - cx;
    ax = cp[3].x - cp[0].x - cx - bx;

    cy = 3.0 * (cp[1].y - cp[0].y);
    by = 3.0 * (cp[2].y - cp[1].y) - cy;
    ay = cp[3].y - cp[0].y - cy - by;

    /* 매개변수 값 t에서 곡선 점을 계산한다 */
    tSquared = t * t;
    tCubed = tSquared * t;

    result.x = (ax * tCubed) + (bx * tSquared) + (cx * t) + cp[0].x;
    result.y = (ay * tCubed) + (bx * tSquared) + (cy * t) + cp[0].y;

    return result;
}

/***
* ComputeBezier는 Point2D 구조체 배열을 제어점 cp로부터 만들어진 곡선 점들로
* 채운다. 호출부는 충분한 메모리를 할당해야 하며, 그 크기는
* "sizeof(Point2D) * numberOfPoints"
***/
void ComputeBezier(Point2D* cp, int numberOfPoints, Point2D* curve)
{
    float dt;
    int i;

    dt = 1.0 / (numberOfPoints - 1);

    for(i = 0; i < numberOfPoints; i++)
        curve[i] = PointOnCubicBezier(cp, i * dt);
}
 
베지어 곡선은 애니메이션 등에서 물체의 운동 경로를 나타낼때도 쓰인다. 여기서 곡선의 x, y 위치는 곡선 그리기가 아닌, 그래픽을 위치시키는 데 사용된다. 이런 방식으로 사용되면 연속적인 점들 사이의 거리가 중요하게 되는데, 보통 이 점들은 등간격이 아니다 ─ 점들은 제어점들이 가까우면 보다 밀집하고, 제어점들이 더 넓게 분포하면 넓게 뿌려질 것이다. 선운동 속력 (linear motion speed) 이 필요하면, 경로를 따라 결과점들을 고르게 뿌리기 위한 추가 처리를 해야 한다.
 
4. 유리 베지어 곡선 (Rational Bézier curves)
───────────────────────────────────────────
    위에서 말한것처럼, 원처럼 단순해 보이는 곡선은 베지어 곡선으로 근사할 수는 있지만 정확히 표현할 수는 없기에 (비록 그 차이는 작고 허용될만한 정도지만) 추가적인 자유도가 필요하다. 유리 베지어 곡선은 임의의 모형에 더 가깝도록 조절가능한 가중치를 더한다. 분자는 가중 번스타인 형식 (weighted Bernstein-form) 의 베지어 곡선이고, 분모는 번스타인 다항식의 가중합 (weighted sum) 이다.
    n + 1개의 제어점 Pi가 주어졌을 때, 유리 베지어 곡선은 다음처럼 표현하거나:
또는 단순히 아래처럼 표현할 수 있다:

댓글 없음: