OpenGL-Assimp-Bullet 座標系マトリクスのコンバート

進まない。ハマり気味です・・・。

マトリクス演算のクラスって便利だけど、種類が多い。
4×4のマトリクスだと、OpenGLではGlfloat[16]で始まり、assimp(3Dモデルローダライブラリ)ではaiMatrix4x4,bullet(物理演算ライブラリ)ではbtTransform。ちなみにopenFrameworksではofMatrix4x4となる。各ライブラリが独自の実装を行なっているから面倒なんだけど、回転移動スケーリング、算術オペレータなど、同じ機能があって同じ使い方ができる。色々使う場合、相互変換がちゃんとできないと困るので、下記にまとめておく。

とにかくGLfloat[16] をベースに変換していけば分かりやすい。内部的に何らかのかたちで4×4で値を保持しているわけだが、OpenGLのマトリクスをtransposeしないと使えないケースが多いので注意が必要。assimpやmascotcapsleなどもtransposeが必要だった。

まず、OpenGLの現在の状態を得る場合下記のようにする。

GLfloat m[16];
glGetFloatv(GL_MODELVIEW_MATRIX, m);

assimpに変換Transposeが必要。a1-d4の並びに注意

aiMatrix4x4 aim= aiMatrix4x4(m[0], m[1], m[2], m[3],
m[4], m[5], m[6], m[7],
m[8], m[9], m[10], m[11],
m[12],m[13], m[14],m[15]);
aim.Transpose();

もしくは

aiMatrix4x4 aim= aiMatrix4x4(m[0], m[4], m[8], m[12],
m[1], m[5], m[9], m[13],
m[2], m[6], m[10], m[14],
m[3],m[7], m[11],m[15]);

bulletに変換。関数があるので楽勝

btTransform btm;
btm.setFromOpenGLMatrix(m);

逆の場合。

assimpからGLfloat[16]

m[0]=aim.a1;
m[1]=aim.b1;
m[2]=aim.c1;
m[3]=aim.d1;
m[4]=aim.a2;
m[5]=aim.b2;
m[6]=aim.c2;
m[7]=aim.d2;
m[8]=aim.a3;
m[9]=aim.b3;
m[10]=aim.c3;
m[11]=aim.d3;
m[12]=aim.a4;
m[13]=aim.b4;
m[14]=aim.c4;
m[15]=aim.d4;

bulletからGLfloat[16]

btm.getOpenGLMatrix(m);

でもってOpenGlに適用する。

glPushMatrix();
glMultMatrixf(m);
/*描画*/
glPopMatrix();

これでbtTransformからのaiMatrix4x4とか、自由自在にできる。

Geometory Shader でB-スプライン曲線を描いてみる

3Dでスプライン曲線を描きたいというお題。

いいサンプルが見つからなかったので、とりあえずofx3DSplineをつくってみた。基本的には頂点配列を更新するとスプライン描画用頂点配列を更新してくれるクラス。でも、重かった。60fpsだと256頂点で20分割の補完が限界。これはリアルタイムに曲線をアニメーションさせ続けた場合の負荷。描画はVBOで高速にやってるのでCPUでやってるスプラインの計算がイケてないことは間違いない。他にもいろいろ計算させたいのでこれでは使い物にならない。

そこで、最近ハマってるシェーダーの登場。今回はあまり使ったことのないGeometryシェーダを使ってみる。ちなみにスプラインをやりたいのにB−スプライン曲線になってるのは力不足のため。本当にやりたいのは指定点を必ず通過するほうのスプライン曲線。こちらのほうがアニメーションでは制御しやすそうな気がしてる。あとこの分割ってところはTessellation シェーダーの役割のような気がしていて、実際できるみたいなんだけど、情報が少なかったのとATIとNvidiaで違うところがあるらしいので、今回はGeometryで。

スプラインの参考にしたのはこのドキュメント

以下、GLSL4でB-スプラインを描くジオメトリシェーダー。uniformで頂点の分割数を変更可能。


layout( lines_adjacency ) in;
layout( line_strip, max_vertices=200 ) out;
uniform int division;

vec4 w_bspline(float alpha){
 vec4 tmp;
 float a2=alpha*alpha;
 float a3=alpha*alpha*alpha;
 tmp.x = -a3 + 3.0*a2 - 3.0*alpha + 1.0;
 tmp.y = 3.0*a3 - 6.0*a2 + 4.0;
 tmp.z = -3.0*a3 + 3.0*a2 + 3.0*alpha + 1.0;
 tmp.w = a3;
 return tmp;
}

void main(void)
{
 int i;
 vec4 w;
 for (i=0; i<=division; i++)
 {
 w = w_bspline(float(i)/float(division));
 gl_Position.xyzw =
 w.x * gl_PositionIn[0].xyzw +
 w.y * gl_PositionIn[1].xyzw +
 w.z * gl_PositionIn[2].xyzw +
 w.w * gl_PositionIn[3].xyzw;
 EmitVertex();
 }
}

注意点はOPENGLのほうで、GL_LINE_STRIP_ADJACENCYを指定すること、これによってシェーダーが隣接含め4点を受け取り補完を行える。こんなかんじで。


 glEnableClientState(GL_VERTEX_ARRAY);
 glVertexPointer(3, GL_FLOAT, 0, mSplineVtx);
 glDrawArrays(GL_LINE_STRIP_ADJACENCY,0,mSplineVtxNum);
 glDisableClientState(GL_VERTEX_ARRAY);

ちなみにopenframeworksのofShaderはジオメトリシェーダーにもちゃんと対応してて、以下のように読み込めるようになってた。Tessellation はいまのところ未対応。


shader.load("myShader.vert","myShader.frag","myShader.geom");

以上でベジェ曲線の描画は上手く行ったんだけど、やっぱり重かったw。CPUでやるのとパフォーマンスそんなに変わってない。やはり若干滑らかさを犠牲にするしか無いかな。そもそもB-スプラインじゃだめだしw

参考:

Bulletでsoftbody

ソフトボディをやってみたかったので、とりあえ本買ってみた。

この本、泣けるぐらいSoftBodyの解説は少なかったわ( 3ページぐらい)w
けど、モデル(wavefront*.obj)のメッシュからsoftbody生成するサンプルがあった。まぁbulletライブラリのサンプルでもsoftbodyのサンプルいくつかあるんだけどね・・・。この本のサンプルはCode::Blocks+MinGWという構成だが、とりあえずVisualStudioで動かしてみた。

なんか表示は上手く行ってないっぽかったけど、上は柔軟体と鋼体のteapot。ぐにゃぐにゃしてる。(っぽい)
softbodyを使うためにはいつも使ってるbtDiscreteDynamicsWorld じゃなくて btSoftRigidDynamicsWorldを使う。当然RigidBodyも使えるワールドだが、softの計算もやるのでパフォーマンスをちょっと心配したけど、軽く試したところRigidについてはそんなに悪くなさそうだった。が、物体をすり抜けてしまうことが多い気がした。通常の物理演算が鋼体の衝突で実現されているのに対してこっちはMeshで頂点の当たり判定を細かくやってるイメージ。ただし、softbodyのオブジェクトの自分自身の当たり判定はほとんど突き抜けてしまっているので、判定が無いか、甘い印象。

で、いつもどおりopenframeworksで使ってみたかったので、ofxBulletで移植を始めたんだけど例のごとく先駆者がいらっしゃいました。

https://github.com/damiannz/ofxBullet/

このライブラリではassimpでロードしたメッシュをもとにsoftbodyを作るとことまでやってくれる。とてもいい。

手前のteapodがぐにゃんぐにゃんしてる。やっぱsoftbodyは突き抜けがおおい。このアドオンの調整も色々必要になりそう。あとはこれを動かしたいわけだけど、ノードがわかれば鋼体と同じ要領で特定の頂点に対して力を加えることができるようだ。

softbody ->addForce(const btVector3& forceVector,int node);

パフォーマンスは少し心配。teapotだと、最近のマシンでも10個も入れるとFPSが結構落ちてくる。あと、柔軟体をワールドに投下するタイミングでも、頂点が多いからか一瞬固まってしまうのも気になる。動きは60FPSでてれば結構気持ちがいい。なんか楽しいことはできそうだ。