始点と終点があるとき、なめらかな曲線を引きたい場合があります。
制御点を指定することで、それを実現する方法が、ベジェ曲線
を利用する方法です。
本記事ではC++
と、ライブラリのhlslpp
を使用して、ベジェ曲線
の実装サンプルコードを紹介したいと思います。
hlslpp
セットアップ方法は下記の記事からご参照ください。
バーンスタイン基底関数
ベジェ曲線を構成する各通過点は、各制御点にバーンスタイン基底関数
というものを乗算したものの総和で求められます。
つまり、パラメトリック変数t
を使用した補間が可能であるということです。
どうやらバーンスタイン基底関数
を式で表すとこのように表せるようです。
B_{i,n}(t) = \binom{n}{i} t^i (1 - t)^{n - i}
とりあえず、基底関数をコードにします。
//---------------------------------------------------------------------------------
//! 二項係数(nCk)を計算する関数
//---------------------------------------------------------------------------------
int BinomialCoefficient(int n, int k)
{
if(k == 0 || k == n)
return 1;
int res = 1;
for(int i = 1; i <= k; ++i) {
res *= (n - i + 1);
res /= i;
}
return res;
}
//---------------------------------------------------------------------------------
//! バーンスタイン基底関数 b_{i,n}(t) を計算
//--------------------------------------------------------------------------------
float BernsteinBasis(int i, int n, float t)
{
float coeff = static_cast<float>(BinomialCoefficient(n, i));
return coeff * powf(t, i) * powf(1.0f - t, n - i);
}
ベジェ曲線
制御点と、パラメトリック変数を渡すことで、ベジェ曲線の通過点を求める関数を記述してみます。
//---------------------------------------------------------------------------------
//! 3Dのベジェ曲線を生成する関数
//--------------------------------------------------------------------------------
float3 Bezier3D(const std::vector<float3>& control_points, float t)
{
int n = static_cast<int>(control_points.size()) - 1;
float3 point = float3(0.0f, 0.0f, 0.0f);
// 各制御点に対応するバーンスタイン基底関数を掛けて加算
for(int i = 0; i <= n; ++i) {
float b = BernsteinBasis(i, n, t);
point += b * control_points[i];
}
return point;
}
上記の場合はfloat3
なので3Dですが、float2
にすれば、2Dでも曲線を表現することが可能です。
総括
- ベジェ曲線の性質を利用することで、始点と終点、制御点から曲線を描くことが出来る。
- 制御点と
バーンスタイン基底関数
の乗算結果の総和で、通過点を求めることが出来る。 -
t
の値を変更することで、補間が可能。