안녕하세요.
4-5개의 Point를 지정하면 그 점을 지나는 부드러운 곡선이 필요해서 검색을 좀 해봤습니다. Catmull-Rom Spline이 제가 원하는 결과에 가장 근접해서 여기저기 정보를 참고해서 만들어 봤습니다. 이게 베이지어 곡선과 다른 점은 베이지어 곡선은 중간중간의 Point를 지나지 않는 근접한 곡선을 그리지만 Catmull-Rom은 Point를 모두 지나도록 되어 있습니다.(스샷 참고)
게임에서는 AI 케릭터가 이동 경로를 부드럽게 움직이도록 하는데 사용한다는군요. 이동 경로를 몇군데 지정하면 직선으로 달리다가 휙 꺽어서 또 직선으로 달리는게 아니라 자연스럽게 움직이도록...
DirectX에 있는 API나 몇몇 공개된 소스들은 4개의 Point값만을 입력받을 수 있게 되어있고 4개 이상의 Point를 사용하려면 두번째 세번째 Point를 계속해서 연결시켜줘야 하는데 그 부분도 같이 구현했습니다. 필요하신 분이 있을지 모르겠지만 제가 찾는데 애 좀 먹어서 공유하고자 올립니다.
(코드 대부분을 참고한 사이트가 있는데 다시 찾으니 보이지 않는군요...)
TPoint tangent(TPoint p1, TPoint p2)
{
return TPoint((p1.x - p2.x) / 2, (p1.y - p2.y) / 2);
}
void Curve(TCanvas *Canvas, TPoint *p, int num, float rez)
{
int px = 0, py = 0, n=0;
float tt, _1_t, _2t, h00, h10, h01, h11;
TPoint m0;
TPoint m1;
TPoint m2;
TPoint m3;
Canvas->Brush->Color = clRed;
for (n=0; nFillRect(TRect(p[n].x-2, p[n].y-2, p[n].x+2, p[n].y+2));
for (float t=0; t<1; t+=rez)
{
tt = t * t;
_1_t = 1 - t;
_2t = 2 * t;
h00 = (1 + _2t) * (_1_t) * (_1_t);
h10 = t * (_1_t) * (_1_t);
h01 = tt * (3 - _2t);
h11 = tt * (t - 1);
if (!n)
{
m0 = tangent(p[n+1], p[n]);
m1 = tangent(p[n+2], p[n]);
px = h00 * p[n].x + h10 * m0.x + h01 * p[n+1].x + h11 * m1.x;
py = h00 * p[n].y + h10 * m0.y + h01 * p[n+1].y + h11 * m1.y;
Canvas->Pixels[px][py] = clBlack;
}
else if (n < num-2)
{
m1 = tangent(p[n+1], p[n-1]);
m2 = tangent(p[n+2], p[n]);
px = h00 * p[n].x + h10 * m1.x + h01 * p[n+1].x + h11 * m2.x;
py = h00 * p[n].y + h10 * m1.y + h01 * p[n+1].y + h11 * m2.y;
Canvas->Pixels[px][py] = clBlack;
}
else if (n == num-1)
{
m2 = tangent(p[n], p[n-2]);
m3 = tangent(p[n], p[n-1]);
px = h00 * p[n-1].x + h10 * m2.x + h01 * p[n].x + h11 * m3.x;
py = h00 * p[n-1].y + h10 * m2.y + h01 * p[n].y + h11 * m3.y;
Canvas->Pixels[px][py] = clBlack;
}
}
}
}
void __fastcall TForm1::Button1Click(TObject *Sender)
{
TPoint p[7] = {TPoint(100,100), TPoint(120,180), TPoint(220,180), TPoint(250,250), TPoint(300,150), TPoint(350,200), TPoint(400,100)};
Curve(Form1->Canvas, p, 7, 0.005);
}
곡선을 그리는 함수입니다.
void Curve(TCanvas *Canvas, TPoint *p, int num, float rez)
p에는 TPoint 배열을, num에 Point 갯수를 넣으면 됩니다. rez값으로 두 점 사이에 몇개의 점을 찍느냐가 결정되는데 작을수록 더 많은 점이 찍힙니다.(rez = 0~1 사이의 값)
(이상하게 코드 일부가 깨져 보이는 군요... cpp 파일도 첨부합니다.)
옛날에 보간법이 필요한 프로젝트를 한적이 있었는데.. 그때 "Catmull-Rom Spline"라는게 있는줄 몰랐는데..
"Catmull-Rom Spline"을 썼으면 더 좋았을듯 하네요.