Posted at

[数学] 3D空間にベジェ曲線を描く

More than 1 year has passed since last update.

ただのベジェ曲線のメモですw

個人的にはこの記事(中学生でもわかるベジェ曲線)が一番分かりやすい解説だと思います。

2次、3次のベジェ曲線についてアニメーションを使って解説してくれているサイトです。

イメージをするだけなら、この記事を読むのが一番早いのではないかなと思ってます。


概要

ということで概要。

ベジェ曲線をWikipediaで調べると以下のように記載があります。


ベジェ曲線(ベジェきょくせん、Bézier Curve)またはベジエ曲線とは、N 個の制御点から得られる N - 1 次曲線である。フランスの自動車メーカー、シトロエン社のド・カステリョ (Paul de Casteljau) とルノー社のピエール・ベジェ (Pierre Bézier) により別々に考案された。ド・カステリョの方が先んじていたが、その論文が公知とならなかったためベジェの名が冠されている[1]。コンピューター上で滑らかな曲線を描くのに2次ベジェ曲線 (Quadratic Bézier curve) や 3次ベジェ曲線 (Cubic Bézier curve) などが広く利用されている。


さらに引用になりますが、定義としては以下のようになります。


制御点を $B0, B1, ..., B_{N-1}$ とすると、ベジェ曲線は、


{\mathbf  {P}}(t)=\sum _{{i=0}}^{{N-1}}{\mathbf  {B}}_{i}J_{{(N-1)i}}(t)

式としては計算したい対象となる制御点 $1 ~ (N - 1)$ の範囲についての合計($\sum$)として、$t$ を媒介変数の関数として表すことができることを示しています。

ちなみにこの $J_{ni}(t)$ は「バーンスタイン基底関数」と呼ばれる「ブレンディング関数」です。

これを式にすると、

J_{ni}(t) = {n \choose i}t^i(1 - t)^{n-i}

ここで $t$ の値は $0 - 1$ の間で変化するとき、両端点は $B_0, B_{N-1}$ を通り、それ以外の制御点が通らないのが一般的となります。

なお、${n \choose i}$ は「二項係数」と呼ばれる項となります。

この「二項係数」を計算する方法はいくつかあるようですが、読んでいても書いても一番簡単なのは以下の式でしょう。

\textstyle \binom{n}{k} = \frac{n!}{k!\,(n-k)!}

Wikipediaには


最後に、計算論的には不利だが、短く書けるのでしばしば証明や導出に用いられるよく知られた階乗函数を用いた式は


と記載があります。

それ以外は特にむずかしいものはないでしょう。

ただとはいえ、数式だけだとプログラムに持ち込むのに戸惑うかもしれないので、具体的なプログラムコードを示しておこうと思います。

using System.Collections;

namespace Utility
{
/// <summary>
/// ベジェ曲線を描く
/// </summary>
public class BezierCurve
{
public Vector3[] ControlPoints { get; set; }

/// <summary>
/// コンストラクタ
/// </summary>
public BezierCurve(Vector3[] controlPoints)
{
ControlPoints = controlPoints;
}

/// <summary>
/// ベジェ曲線関数
/// </summary>
public Vector3 Evaluate(float t)
{
if (ControlPoints == null)
{
return Vector3.zero;
}

Vector3 result = Vector3.zero;
int n = ControlPoints.Length;
for (int i = 0; i < n; i++)
{
result += ControlPoints[i] * Bernstein(n - 1, i, t);
}

return result;
}

/// <summary>
/// バーンスタイン基底関数
/// </summary>
static float Bernstein(int n, int i, float t)
{
return Binomial(n, i) * Mathf.Pow(t, i) * Mathf.Pow(1 - t, n - i);
}

/// <summary>
/// 二項係数を計算する
/// </summary>
static float Binomial(int n, int k)
{
return Factorial(n) / (Factorial(k) * Factorial(n - k));
}

/// <summary>
/// 階乗を計算する
/// </summary>
static float Factorial(int a)
{
float result = 1f;
for (int i = 2; i <= a; i++)
{
result *= i;
}

return result;
}
}
}