頂点スキニング

SAMPLE https://www.dropbox.com/s/v5gwxqgocrwjlmb/skinning.exe( 頂点スキニング ) https://www.dropbox.com/s/wwjxxfaa2t5xcdm/spring.exe( 頂点スキニングを使ったバネモデル )
App::onDraw() { float m0[16], m1[16]; // 現在の骨の位置に移動する。 glLoadIdentity(); glTranslatef( 0, 50, 0 ); // 回転する。 glRotatef( m_timer, 0,0,1 ); // 骨の位置を基準(Pivot)にして回転するため、逆行列をかけて原点に移動する。 glTranslatef( 0, -50, 0 ); glGetFloatv( GL_MODELVIEW_MATRIX, mb0 ); // 2番目の骨も同じように計算 glLoadIdentity(); glTranslatef( 100, 50, 0 ); glRotatef( m_timer, 0,0,1 ); glTranslatef( -100, -50, 0 ); glGetFloatv( GL_MODELVIEW_MATRIX, mb1 ); { CGparameter param; // 頂点シェーダで変形させるため、骨の行列をシェーダに渡す。 float m[] = { m0[0], m0[4], m0[ 8], m0[12], m0[1], m0[5], m0[ 9], m0[13], m0[2], m0[6], m0[10], m0[14], m0[3], m0[7], m0[11], m0[15], m1[0], m1[4], m1[ 8], m1[12], m1[1], m1[5], m1[ 9], m1[13], m1[2], m1[6], m1[10], m1[14], m1[3], m1[7], m1[11], m1[15], }; param = cgGetNamedParameter( shdV, "m"); cgGLSetParameterArray4f( param, 0, 2*4, m ); // モデルビュー, プロジェクションを渡す。 glMatrixMode( GL_MODELVIEW_MATRIX ); glLoadIdentity(); convertCameraSpace(); param = cgGetNamedParameter( shdV, "mvp"); cgGLSetStateMatrixParameter( param, CG_GL_MODELVIEW_PROJECTION_MATRIX, CG_GL_MATRIX_IDENTITY ); } // 描画する。( ウェイトの値は glNormal で指定しておく。 ) glBegin( GL_TRIANGLE_STRIP ); { glColor4f(1,1,1,1); for( int j=0; j<2*7; j++ ){ // ウェイト値 float w0 = 1 - 1/7.0f*(j/2); float w1 = 1 - w0; glNormal3f( w0, w1, 1 ); float x = 2*(w1 - 0.5f) * 100; float y = 100 * (j%2); glVertex3f( x, y, 0 ); } } glEnd(); }
頂点シェーダで頂点を変形する
float4x4 mtx0 = float4x4( m[0], m[1], m[2], m[3] ); float4x4 mtx1 = float4x4( m[4], m[5], m[6], m[7] ); // ウェイト float w0 = IN.nrm.x; float w1 = IN.nrm.y; // 変形 float4 p = w0 * mul( mtx0, IN.pos ) + w1 * mul( mtx1, IN.pos ); // モデルビュープロジェクション変換 OUT.pos = mul( mvp, pt );



CPUで変形する

CPUで計算するには頂点座標を指定する( glVertex() )前に変換をしておく。
float v[2*7*3]; for( int j=0; j<2*7; j++ ){ // ウェイト値 float w0 = 1 - 1/7.0f*(j/2); float w1 = 1 - w0; float x = 2*(w1 - 0.5f) * 100; float y = 100 * (j%2); // 行列とベクトルの積 multiMatrix( mb0, v0, v0 ); multiMatrix( mb1, v1, v1 ); // ウェイトをかけてブレンドする v[3*j+0] = w0*v0[0] + w1*v1[0]; v[3*j+1] = w0*v0[1] + w1*v1[1]; v[3*j+2] = 0; } glBegin( GL_TRIANGLE_STRIP ); glColor4f(1,1,1,1); for( int j=0; j<2*7; j++ ){ // 事前計算した頂点を指定する glVertex3fv( v + 3*j ); } glEnd();
行列とベクトルの積には glMultMatrix() を利用する。
void multiMatrix( const float *m, float *v, float *vout ) { glMatrixMode( GL_MODELVIEW ); glLoadMatrixf( m ); float mt[16] = { v[0], v[1], v[2], 1, // 第1列をベクトル扱いにする。 0, 0, 0, 0, // 残りの3列は仮 0, 0, 0, 0, 0, 0, 0, 0, }; // 右からかける glMultMatrixf( mt ); // 計算結果として行列同士の積の結果の第一列を取得する glGetFloatv(GL_MODELVIEW_MATRIX, mt ); vout[0] = mt[0]; vout[1] = mt[1]; vout[2] = mt[2]; }



頂点配列でウェイトを指定する

頂点の属性としてウェイトを指定するには glVertexAttribPointer() を利用して 汎用型の属性のひとつとして指定する。
App::onInit() { glVertexAttribPointer = (PFNGLVERTEXATTRIBPOINTERPROC)wglGetProcAddress("glVertexAttribPointer"); } # define ATTR1 1 App::onDraw() { struct Vertex { float pos[3]; float col[3]; float weight[4]; }; Vertex v[2*NR]; { for( int i=0; i<2*NR; i++ ){ float w0 = 1 - 1.0f/(NR-1)*(i/2); float w1 = 1 - w0; float x = 2*(w1 - 0.5f) * 100; float y = 100 * (i%2); v[i].pos[0] = x; v[i].pos[1] = y; v[i].pos[2] = 0; v[i].col[0] = 1; v[i].col[1] = 0; v[i].col[2] = 0; v[i].weight[0] = w0; } } glEnableClientState( GL_VERTEX_ARRAY ); glEnableClientState( GL_COLOR_ARRAY ); glEnableVertexAttribArray( ATTR1 ); glVertexPointer( 3, GL_FLOAT, sizeof(Vertex), v ); glColorPointer ( 3, GL_FLOAT, sizeof(Vertex), v[0].col ); glVertexAttribPointer( ATTR1, 1, GL_FLOAT, false, sizeof(Vertex), v[0].weight ); // シェーダを有効にする。 cgGLEnableProfile( pfV ); glDrawArrays( GL_TRIANGLE_STRIP, 0, m_nr*2 ); }
頂点シェーダではアプリケーションで指定した頂点インデックスからウェイトを取得する。
struct app { float4 pos : POSITION; float4 weight : ATTR1; }; float4x4 mtx0 = float4x4( m[0], m[1], m[2], m[3] ); float4x4 mtx1 = float4x4( m[4], m[5], m[6], m[7] ); // ウェイト float w0 = IN.weight[0] float w1 = 1 - w0; // 変形 float4 p = w0 * mul( mtx0, IN.pos ) + w1 * mul( mtx1, IN.pos ); // モデルビュープロジェクション変換 OUT.pos = mul( mvp, p );