Microsoft Visual Studio .NET 2003 |
■パラメトリック曲線 | Prev Top Next |
関連ページ:なし |
ベジェ曲線は、n 個の制御点から得られる n - 1 次曲線です。 コンピューター上で滑らかな曲線を描くのに広く利用されています。
ようは複雑な形の曲線の描画に使用できるということです。曲線の制御は非常に簡単で n 個の制御点とt (0 < t < 1) の比率を示すパラメータが1つあればOKです。 また曲線の描画だけでなくtの値を変化させることによりベジェ曲線上で自由に移動させることも可能です。 自作のゲーム「GrowWing」では敵キャラの移動に使用しています。 Direct3Dに直接かかわるネタではないですが、重要なことなので紹介します。
図1
図1はベジェ曲線のサンプルです。
赤直線:ベジェ曲線
青● :制御点の開始、終了地点
青〇 :制御点
黒直線:制御点を結ぶ直線
このように滑らかに曲線が描かれています。特徴的なのは青〇の部分をベジェ曲線が通らないところです。 まあこのせいで調整が大変だったりしますけどね。
数式です。
Σni=0 nCi ti(1-t)n-i Pi
但し、
n!
nCi = ―――――, n! = n (n-1)・・・3 2 1
i !(n-i) !
nは次数です。制御点数 - 1となります。したがって図1のサンプルでは4次曲線となります。
t (0 < t < 1) は比率パラメータです。
Pは制御点です。
数式の意味についての解説は省略の方向で。
//階乗計算 unsigned long GetFactorial( unsigned long n ) { if( n > 1 ) return n * GetFactorial( n - 1 ); else if( n == 1 ) return 1; return 0; } D3DXVECTOR3 GetBezier( float t, //比率の位置パラメータ( 0.0f <= t <= 1.0f ) D3DXVECTOR3* P, //制御点配列 const int PCnt //制御点数 ) { D3DXVECTOR3 v; v.x = 0.0f; v.y = 0.0f; v.z = 0.0f; try { if( PCnt >= 2 ) { for( int i=0; i<PCnt; i++ ) { //階乗計算 double c = 1.0; if( i != 0 && i != PCnt - 1 ) c = (double)GetFactorial( PCnt - 1 ) / (double)GetFactorial( i ) / (double)GetFactorial( PCnt - 1 - i ); //t^i * (1-t)^(n-1-i) float f = powf( t, (float)i ) * powf( ( 1.0f - t ), (float)( PCnt - 1 - i ) ); v.x += (float)c * f * P[i].x; v.y += (float)c * f * P[i].y; v.z += (float)c * f * P[i].z; } } } catch( System::Exception* e ) { Debug::WriteLine( e->ToString() ); } return v; }このソースサンプルは、マネージドコードです。必要に応じて修正してください。
i = 0 または PCnt - 1 - i = 0 のとき 0 除算しないようにしています。この場合、計算を行わず変数cに1を代入しています。
多分これであってると思うけど間違ってたら連絡ください。
あと注意点としては、階乗計算を行うため計算途中の値が非常に大きくなる可能性があります。したがって制御点数はあまり多くしないようにした方がよいでしょう。
引き続きパラメトリック曲線です。Ferguson / Coons 曲線を紹介します。 ベジェ曲線では制御点と比率をパラメータとして曲線を描画しました。今回のFerguson / Coons 曲線では 開始位置、終了位置、開始位置の速度ベクトル、終了位置の速度ベクトル、比率をパラメータとします。
図2
パラメータをいろいろいじってみましたが、結構いろんな形の曲線が描けるみたいで面白かったです。制御はベジェ曲線以上に困難ですが。
計算式は
┌ 2 -2 1 1┐┌ x0 ┐ x(t) = [t3 t2 t 1]│-3 3 -2 -1││ x1 │ │ 0 0 1 0││ v0 │ └ 1 0 0 0┘└ v1 ┘です。
D3DXVECTOR3 GetFerguson_Coons( float t, //比率の位置パラメータ( 0.0f <= t <= 1.0f ) D3DXVECTOR3* p0, //開始位置(添え字 = 0)と終了位置(添え字 = 1) D3DXVECTOR3* v0 //開始位置の速度ベクトル(添え字 = 0)と終了位置の速度ベクトル(添え字 = 1) ) { D3DXVECTOR3 v; v.x = 0.0f; v.y = 0.0f; v.z = 0.0f; try { float t3 = powf( t, 3.0f ); float t2 = powf( t, 2.0f ); float t1 = t; float t0 = 1; float f0 = t3 * 2.0f + t2 * -3.0f + t1 * 0.0f + t0 * 1.0f; float f1 = t3 * -2.0f + t2 * 3.0f + t1 * 0.0f + t0 * 0.0f; float f2 = t3 * 1.0f + t2 * -2.0f + t1 * 1.0f + t0 * 0.0f; float f3 = t3 * 1.0f + t2 * -1.0f + t1 * 0.0f + t0 * 0.0f; v.x = f0 * p0[0].x + f1 * p0[1].x + f2 * v0[0].x + f3 * v0[1].x; v.y = f0 * p0[0].y + f1 * p0[1].y + f2 * v0[0].y + f3 * v0[1].y; v.z = f0 * p0[0].z + f1 * p0[1].z + f2 * v0[0].z + f3 * v0[1].z; } catch( System::Exception* e ) { Debug::WriteLine( e->ToString() ); } return v; }ソースはこんな感じです。
参考サイト:t-pot