茂加部珈琲店

主にtech関連のメモ置き場です

NURBS曲線の特徴

CGの分野では解像度に非依存な形状を保存するために、パラメトリック曲線を利用することがあります. 今回は、その中でも特に自由度の高いNURBS曲線の基本的な事項をおさらいします.

ベクタ形式ではBezier曲線が有名で、古くから使われていますが、Bezier曲線にはいくつかの欠点があります. その中でも代表的なものを纏めてみると

  • 次数が制御点数に影響される
  • 重み付けができない
  • 局所的な変更が難しい

これらの問題点を解決するために考案されたのが、BSpline曲線です. NURBS曲線(Non-Uniform Rational BSpline curve)とは、名前の通りBSpline曲線に便利な特性(重み付けと非一様ノット列)を追加した形のことを言います.
NURBS曲線はBezier曲線とBspline曲線の上位版にあたり、高度な表現力をもちながら扱いやすい表現です

NURBS曲線の定義

NURBS曲線は以下の式で表現されます.
{ \displaystyle
C(u) = \frac{\sum_{i=0}^{k-1} N_{i,n}(u)w_iP_i } {\sum_{i=0}^{k-1} N_{i,n}(u)w_i}
}
ただし、kは制御点の個数, nは次数です {N_{i,n}}(u)はBSpline基底関数と呼ばれ、以下のように定義されます

{n=0}のとき
 { \displaystyle
\begin{equation}N_{i,n}(u)= \left \{\begin{array}{l} 1 (u_i \leq u \lt u_{i+1} ) \\ 0 (otherwise) \end{array} \right.\end{equation}
}

{n\neq0}のとき

 { \displaystyle
N_{i,n}(u)= \frac{u-u_i}{u_{i+n}-u_i}N_{i,n-1}(u)+\frac{u_{i+n+1}-u}{u_{i+n+1}-u_{i+1}}N_{i+1,n-1}(u)
}

ここで、{n=0}のときに1を返す範囲は、

{  \displaystyle
(u_i \leq u \lt u_{i+1})
}

{  \displaystyle
(u_i \lt u \leq u_{i+1})
}

のどちらかを採用します.
区間になっているほうの端は範囲に含まれなくなるので、エラー処理を行う必要があることに注意してください. また、

{  \displaystyle
(u_i \leq u \leq u_{i+1})
}

を採用して、{n=1}で例外処理を行う方法もあります

以下に基底関数の素直な実装を載せておきます.(改良の余地はあります.エラー処理はお好みで追加してください)
今度はDeBoorのアルゴリズムとかをやりたいですね

/** @fun
 * @brief BSpline基底関数を計算します
 * @param t 独立変数t
 * @param i 制御点の番号i
 * @param n 次数n
 * @param knots ノットベクトル
 */
double NURBS::basis(double t, size_t i, size_t n,
                    const std::vector<double> &knots) {

  // N[i][0]
  if (n == 0)
    return (knots[i] <= t && t < knots[i + 1])
               ? 1
               : 0;

  double a1 = knots[i + n] - knots[i];
  double a2 = knots[i + n + 1] - knots[i + 1];

  double b1 = t - knots[i];
  double b2 = knots[i + n + 1] - t;

  // "ゼロで割られたらゼロ"という決まりです
  double r1 = (b1 == 0) ? 0 : basis(t, i, n - 1, knots) * b1 / a1;
  double r2 = (b2 == 0) ? 0 : basis(t, i + 1, n - 1, knots) * b2 / a2;

  return r1 + r2;
}