正月の成果

アバター
GRAM
記事: 164
登録日時: 14年前
住所: 大阪

正月の成果

投稿記事 by GRAM » 14年前

あけましておめでとうございます
今年もよろしくお願いしますm(_ _)m


さて正月何をしていたかって、DirectX June2010のSkinnedMeshサンプルと格闘していました
人生でこれほど目が痛くなった正月はない…

まったく…Tinyは男なのか女なのか結局わからなかったw
理解はどこら辺まで深まったのかというと、「固定機能インデックスなしスキニングによるレンダリング」は(おそらく)理解できました!
1週間近くかかったorz
「ゲームつくろー!」というありがたいサイト様で何点かわからなかった所が詳しく解説されているのを数日前に発見し、後は何とかソースコードを追う事が出来た…

忘れないようメモしておく
どうやら肝となる構造体は
struct D3DXFRAME_DERIVED : public D3DXFRAMEこいつと
struct D3DXMESHCONTAINER_DERIVED : public D3DXMESHCONTAINERこいつと
class CAllocateHierarchy : public ID3DXAllocateHierarchyこいつらしい

D3DXFRAME_DERIVED は結局

CODE:

{
//D3DXFRAMEで定義
    LPSTR		Name;				//フレームの名前
    D3DXMATRIX		TransformationMatrix;		//座標変換用行列(自分のフレームの座標系から親のフレームの座標系へ)

    LPD3DXMESHCONTAINER	pMeshContainer;			//上の2番目の構造体::メッシュコンテナへのポインタ

    struct _D3DXFRAME	*pFrameSibling;			//兄弟となるフレームへのポインタ
    struct _D3DXFRAME	*pFrameFirstChild;		//子となるフレームへのポインタ
//D3DXFRAME_DERIVEDで定義
    D3DXMATRIXA16 	CombinedTransformationMatrix;	//自分のフレームの座標系から一気にローカル座標系へ座標変換する行列
}
を持っていることになるみたい。
次にD3DXMESHCONTAINER_DERIVEDは

CODE:

{
//D3DXMESHCONTAINERで定義
    LPSTR                   Name;		    //メッシュコンテナの名前
    D3DXMESHDATA            MeshData;		//メッシュのデータ本体(後に最適化されたものが入る)
    LPD3DXMATERIAL          pMaterials;		//マテリアル情報へのポインタ
    LPD3DXEFFECTINSTANCE    pEffects;		//エフェクトへのポインタ
    DWORD                   NumMaterials;	//マテリアルの数
    DWORD                  *pAdjacency;		//隣接性情報
    LPD3DXSKININFO          pSkinInfo;		//スキンインフォ?何じゃそら
    struct _D3DXMESHCONTAINER *pNextMeshContainer;	//次のメッシュコンテナへのポインタ(どうやらメッシュコンテナはリストを形成できるみたい)
//D3DXMESHCONTAINER_DERIVEDで定義
    LPDIRECT3DTEXTURE9* ppTextures;		   // array of textures, entries are NULL if no texture specified    
    // SkinMesh info				       //↑テクスチャの配列、テクスチャがなければNULLが入ってる
    LPD3DXMESH pOrigMesh;			       //xファイルから読み込んだ最適化前のメッシュへのポインタ
    LPD3DXATTRIBUTERANGE pAttributeTable;        //取り敢えずは固定機能インデックスなしスキニングでは使わない
    DWORD NumAttributeGroups;			   //上に同じ
    DWORD NumInfl;				           //最大何本のボーンが一つの頂点に影響するか
    LPD3DXBUFFER pBoneCombinationBuf;           //ボーンコンビネーションテーブル:LPD3DXBONECOMBINATIONを含むバッファ
    D3DXMATRIX** ppBoneMatrixPtrs;              //ボーン座標からローカル座標へ変換する行列へのポインタ
						                   //上のコンビネーションテーブルにあるボーン番号と配列の並びが対応している
    D3DXMATRIX* pBoneOffsetMatrices;	        //頂点をローカル座標からボーン座標へ一辺に変換する行列の配列(こいつもテーブルのボーン番号と対応済み)
    DWORD NumPaletteEntries;			   //pAttributeTableと同じく取り敢えずパスして大丈夫
    bool UseSoftwareVP;				       //頂点変換をソフトウェアでやるかどうか
    DWORD iAttributeSW;				       //こいつは上の判定に使ってるみたい
}
とたくさん変数がある。
であとはclass CAllocateHierarchyのCreateFrameとCreateMeshContainerというメンバ関数が定義されてる。
どうもCAllocateHierarchyはフレームの階層構造と、メッシュコンテナを作ったり解放したりするインターフェースらしい。
これ自体は何という事もなく前者は単純にD3DXFRAME_DERIVEDを作成して初期化。後者はD3DXMESHCONTAINER_DERIVEDを作ってファイルから読み取った情報を格納するだけ…
とか思ってたらCreateMeshContainerの最後の方でGenerateSkinnedMeshとかいう関数へ入る
この関数が無駄に長い…。でもやっていることはメッシュを最適化してD3DXMESHCONTAINER_DERIVEDで定義されたスキンメッシュ関係のメンバ変数へ値を代入しているだけ
ここでID3DXSkinInfo::ConvertToBlendedMeshというメッシュの最適化関数が呼ばれていて、コンビネーションテーブルや最大影響数が設定される。
(どうやらID3DXSkinInfoは直訳でのスキンの情報だけじゃなくて、最適化なんかも行ってくれるみたい)
これらは全部、引数に肝心のXファイルのファイル名とこれら三つの構造体とクラス、をとるD3DXLoadMeshHierarchyFromXという関数内で呼ばれるらしい

ここまで来て疑問に思ったのが、ボーンとフレームはどう関係しているのかという事だった。どうもそこら辺があいまいだったので悩みながらコードを読んでると
どうやらボーンの座標系とフレームの座標系は同じもののようだった。というよりかはボーンなんてものは概念にすぎないのかな?
実際にはメッシュコンテナのppBoneMatrixPtrsにある行列へのポインタはそれぞれがフレームのCombinedTransformationMatrixを指しているみたい
フレーム毎に更新される行列はメッシュコンテナに概念としてあるボーンじゃなくてフレームの行列だし…
メッシュコンテナにあるボーンオフセット行列は、つまるところローカル座標から初期姿勢のフレームが持つ座標系へ一気に変換する行列。
こいつはXファイル内部に情報があるようでCreateMeshContainerでID3DXSkinInfo内部に格納されている情報がコピーされる。

描写の処理自体は非常にシンプルで逆に驚いたくらいだった
具体的には各頂点をまずメッシュコンテナにあるpBoneOffsetMatricesでボーン座標へ変換
そのあとppBoneMatrixPtrsの行列ででローカル座標へ戻す…をブレンドしながら繰り返しているだけ


結局このスキンメッシュは
①xファイルのファイル名を指定してD3DXLoadMeshHierarchyFromXを呼ぶ
②内部でCAllocateHierarchy::CreateFrameが呼ばれてフレームの階層構造が作られる
③さらにD3DXLoadMeshHierarchyFromX内部でCAllocateHierarchy::CreateMeshContainerがよばれメッシュコンテナが作成される
④CAllocateHierarchy::CreateMeshContainer内でGenerateSkinnedMesh関数が呼ばれ、メッシュの最適化やスキンメッシュの情報(ボーンが最大何本あるかとか)が
  メッシュコンテナに登録される
⑤多分どっかでD3DXFRAMEのpMeshContainerへ作成したメッシュコンテナが登録される
  (実際にはこのサンプルではメッシュコンテナは1つしか作成されず、最下層のフレームにのみ登録されている。それ以外の親のフレームはNULLポインタが入っているようだった)
⑥D3DXLoadMeshHierarchyFromX終了
⑦SetupBoneMatrixPointersが呼ばれる。こいつはフレームのCombinedTransformationMatrixのポインタをメッシュコンテナのppBoneMatrixPtrsのへ登録し
メッシュコンテナ内のコンビネーションテーブル内のボーン番号と、「ボーン座標(つまるところフレームが持つ座標系)からローカル座標への変換行列」が対応するようにする関数。
ここまででセットアップが終わる。

後はフレーム毎に
⑧フレーム内部の座標系の相対位置を変化させる(つまりボーンを動かす)
描写毎に
⑨ボーンオフセット行列pBoneOffsetMatricesで、初期のローカル座標系での頂点座標から初期姿勢のボーン座標系へ頂点座標を変換。
そのあとppBoneMatrixPtrsで現在の姿勢でのローカル座標系へ変換これを頂点ごとに影響するボーンの数だけ繰り返す
を行っている。


…よくできていると言えば良くできているが…誰だこんなの考えたのwもうちょっと何とかならんものか・・・
今回見てわかったのは、いままでボーンを動かすという表現に惑わされてボーンという実態があるかのように感じていたが、実はそれはフレームを動かすということだった事。
実際のところこれはスキンメッシュでも、そうでない機械のような階層のあるメッシュの集合体もあんまり変わんないという事がわかった…。
たぶんこのサンプルのpMeshContainerへパーツごとにメッシュを登録して、描写の処理をちょっと工夫すれば戦車とかは作れそうだ。

・・・!!!書いてて閃いたのだが、メッシュコンテナがリストを形成する事はもしかしてバウンディングボックスとかは、ここへ格納したら良かったりしないか!?とか思ってみる。これはもうちょっと調べる必要がありそうだけど…

ああ
記事: 49
登録日時: 14年前

Re: 正月の成果

投稿記事 by ああ » 14年前

スキンメッシュ・・・、俺に理解出来るだろうか・・・w

夏ぐらいに詳しく教えてくださいw

ISLe
記事: 2650
登録日時: 14年前

Re: 正月の成果

投稿記事 by ISLe » 14年前

ボーンというのは、変形する軸とともに変形したときに周辺の頂点に与える影響の度合いを示すものです。
戦車は固いパーツの集まりなので変換行列に階層を持たせるだけで良いのではないかと思います。
戦車の砲塔の付け根のカバーをリアルに変形させるにはスキンメッシュが必要になるでしょう。

アバター
GRAM
記事: 164
登録日時: 14年前
住所: 大阪

Re: 正月の成果

投稿記事 by GRAM » 14年前

>>NNKさん
いやっいけますって!夏までに…(笑)教えられるレベルに居るといいなぁw逆に教わることになりそうだ^^;

>>ISLeさん
頂点に与える影響度合いを示すものですか…確かにこの表現がしっくりきますね
ありがとうございます。なるほど、覚えておきます。
>>戦車の砲塔の付け根のカバーをリアルに変形させるにはスキンメッシュが必要になるでしょう
そこまでは考えてもいませんでした(笑)やはり本場はいろいろと想像を絶する工夫がありそうですね…