SAMPLE
頂点スキニング
頂点スキニングを使ったバネモデル
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 );