12、ボーン最大数を倍に増やす




この章ではボーンの最大数を倍に増やすための機能追加をします。
11章までは1モデルあたり43個がボーンの最大数でした。
これを86個に増やしました。

シェーダー定数をマトリックスではなく、クォータニオンと移動成分にすることで実現しました。

####### 2012/03/20 14:35 プログラム修正
要望に応えてボーンの最大数を86個から102個に増量しました。
拡張用領域にとっておいたのですが、基本機能を充実させることに使うことにします。
後で足りなくなったら、そのときはオプションで頂点テクスチャを使うことにします。

####### 2012/03/23 8:00 プログラム修正
モデルパネルを新設しました。
今までモーションを付けるモデルの切り替えは、いちいちメニューを選択しないといけませんでしたが
パネルのラジオボタンをワンタッチするだけで切り替えられます。
モデル全体の表示非表示もワンタッチになりました。

####### 2012/03/25 8:30 プログラム修正
ファイルオープンの修正。
rdb,mqoとqubの2つのフィルターにしました。
ファイルは1度に複数個選べるようにしました。
readmeにサンプルの場所を書きました。

####### 2012/04/04 11:35 プログラム修正
マウスでライトの回転が出来なくなっていたのを修正。
  ライトの回転ラジオボタンを選択すれば回転できます。
ライトの矢印の表示非表示設定チェックボックス追加。
F1からF6キーで正面、背面、右面、左面、上面、下面ビューへワンタッチ。

####### 2012/04/11 5:30 プログラム修正
プロジェクトファイルを読み込み時と同じフォルダに保存しようとするとエラーになるバグを修正。

####### 2012/04/15 4:20 プログラム修正
タイムラインのドラッグ時の色などを見やすく修正。
1.0フレーム未満の間隔で近接キーが作成され、モーションが思い通りに編集出来なくなることがある不具合を修正。

####### 2012/04/16 4:00 プログラム修正
矩形選択後、Ctrlドラッグでキーの移動をするときに、キーがなくなることがあるバグを修正。
(タイムラインには表示されているが、内部データが無くなるバグでした)
タイムラインの表示色をさらに修正。


ソースは下のページからダウンロードしてください。
OpenRDBダウンロードページ


解説

この章ではボーンの最大数を倍増させるためのプログラムの説明をします。

ボーンの最大数を決めるものとはなんでしょう?
それはシェーダーの定数の数です。
OpenRDBはボーン変形をシェーダーで行います。
各ボーンの姿勢情報をシェーダーの定数にC++側から設定し
それをシェーダーで参照してボーン変形を行っています。

シェーダーの定数の数はシェーダーのバージョンによって決まっています。
この制限から11章までは43個までのボーンしか使用できませんでした。

この章でもシェーダーの定数の制限は変わりません。
ではなぜ倍増できるかというと、格納するデータの大きさを半分にするのです。

今までは、ボーン1個当たりfloat4x4のデータを使用していました。
4x4の行列データを格納していたのです。

これをfloat4のクォータニオンとfloat3の移動成分に変更します。
これで1ボーンあたりのデータ量が半分になり、ボーンの数を倍増させることが出来るのです。

クォータニオンと移動成分をシェーダーで受け取って
それを4x4行列に変換してブレンドします。

では実際のコードを見てみましょう。

CMotionPoint::UpdateMatrixから以下の関数を呼び出します。

int CMotionPoint::MakeDispMat()
{
m_disptra.x = m_worldmat._41;
m_disptra.y = m_worldmat._42;
m_disptra.z = m_worldmat._43;

D3DXMATRIX tmpwmat = m_worldmat;
tmpwmat._41 = 0.0f;
tmpwmat._42 = 0.0f;
tmpwmat._43 = 0.0f;

D3DXQUATERNION xtmpq;
D3DXQuaternionRotationMatrix( &xtmpq, &tmpwmat );

m_dispq.SetParams( xtmpq );

m_dispmat = m_dispq.MakeRotMatX();
m_dispmat._41 = m_disptra.x;
m_dispmat._42 = m_disptra.y;
m_dispmat._43 = m_disptra.z;

return 0;
}



計算した姿勢の行列m_worldmatを
クォータニオンm_dispqと移動成分m_disptraに分解しています。

これをシェーダーに渡します。

int CModel::SetShaderConst()
{
D3DXQUATERNION boneq[MAXBONENUM];
D3DXVECTOR3 bonetra[MAXBONENUM];

ZeroMemory( boneq, sizeof( D3DXQUATERNION ) * MAXBONENUM );
ZeroMemory( bonetra, sizeof( D3DXVECTOR3 ) * MAXBONENUM );

map<int, CBone*>::iterator itrbone;
int setcnt = 0;
for( itrbone = m_bonelist.begin(); itrbone != m_bonelist.end(); itrbone++ ){
int boneno = itrbone->first;
CBone* curbone = itrbone->second;
if( curbone && (boneno >= 0) && (boneno < MAXBONENUM) ){
boneq[boneno].x = curbone->m_curmp.m_dispq.x;
boneq[boneno].y = curbone->m_curmp.m_dispq.y;
boneq[boneno].z = curbone->m_curmp.m_dispq.z;
boneq[boneno].w = curbone->m_curmp.m_dispq.w;
bonetra[boneno] = curbone->m_curmp.m_disptra;
setcnt++;
}
}

if( setcnt > 0 ){
HRESULT hr;
hr = g_pEffect->SetValue( g_hmBoneQ, (void*)boneq, sizeof( D3DXQUATERNION ) * MAXBONENUM );
if( hr != D3D_OK ){
_ASSERT( 0 );
return 1;
}
hr = g_pEffect->SetValue( g_hmBoneTra, (void*)bonetra, sizeof( D3DXVECTOR3 ) * MAXBONENUM );
if( hr != D3D_OK ){
_ASSERT( 0 );
return 1;
}
}
return 0;
}



後はシェーダーの作業になります。
シェーダーの定数宣言部分は以下のようになります。

float4 g_mBoneQ[86];
float3 g_mBoneTra[86];



そして行列ブレンド部分は

VS_OUTPUT RenderSceneBoneVS( float4 vPos : POSITION, 
float3 vNormal : NORMAL,
float2 vTexCoord0 : TEXCOORD0,
float4 bweight : BLENDWEIGHT,
float4 bindices : BLENDINDICES,
uniform int nNumLights )
{
VS_OUTPUT Output;
float4 wPos;

float4 boneq[4];
float3 bonetra[4];

boneq[0] = g_mBoneQ[bindices.x];
boneq[1] = g_mBoneQ[bindices.y];
boneq[2] = g_mBoneQ[bindices.z];
boneq[3] = g_mBoneQ[bindices.w];

bonetra[0] = g_mBoneTra[bindices.x];
bonetra[1] = g_mBoneTra[bindices.y];
bonetra[2] = g_mBoneTra[bindices.z];
bonetra[3] = g_mBoneTra[bindices.w];

float4x4 finalmat;
finalmat = CalcBlendMat( boneq, bonetra, bweight );

wPos = mul( vPos, finalmat );
Output.Position = mul( wPos, g_mVP );
wPos /= wPos.w;

(以下省略)


影響するボーンのクォータニオンと移動成分をboneqとbonetra配列に格納して
それをCalcBlendMat関数に渡してブレンド行列を計算しています。

CalcBlendMatは以下のような内容です。

float4x4 CalcBlendMat( in float4 boneq[4], in float3 bonetra[4],
      in float4 bweight )
{
float4x4 retmat = 0;

float4x4 tmpmat;
float dat00, dat01, dat02;
float dat10, dat11, dat12;
float dat20, dat21, dat22;
float x, y, z, w;

float weight[4] = { bweight.x, bweight.y, bweight.z, bweight.w };

for(int i=0; i<4; i++ ){
x = boneq[i].x;
y = boneq[i].y;
z = boneq[i].z;
w = boneq[i].w;

dat00 = w * w + x * x - y * y - z * z;
dat01 = 2.0f * ( x * y + w * z );
dat02 = 2.0f * ( x * z - w * y );

dat10 = 2.0f * ( x * y - w * z );
dat11 = w * w - x * x + y * y - z * z;
dat12 = 2.0f * ( y * z + w * x );

dat20 = 2.0f * ( x * z + w * y );
dat21 = 2.0f * ( y * z - w * x );
dat22 = w * w - x * x - y * y + z * z;

tmpmat = float4x4(
dat00, dat01, dat02, 0.0f,
dat10, dat11, dat12, 0.0f,
dat20, dat21, dat22, 0.0f,
bonetra[i].x, bonetra[i].y, bonetra[i].z, 1.0f
);


retmat += tmpmat * weight[i];
}
return retmat;
}


クォータニオンを行列にする部分は実はぼくは完全には理解していませんww
丸覚えです。

とにかくクォータニオンと移動成分から行列を作り
それにブレンド率をかけて足し算してブレンド行列を作ります。

これでボーン最大数倍増です!!





オープンソースのトップに戻る

トップページに戻る