DirectX一覧

SkinnedMeshで複数描画したい

DirectXのサンプルSkinnedMeshをいじって、
リソースは1つ、描画用インスタンスは複数なんてものを作成してみました。 

色々と、DirectXの仕様で躓く点が多かったように思います。

実現の為のポイント

  • AnimationControllerは単純にCloneで作成しても、ボーン構造(FRAME)は共有されてしまう
    • 独立させたいのは、マトリックスパレット.
    • 一緒にボーンオフセット行列もインスタンスごとに保持させてしまうと、楽にはなる。
  • マトリックスパレットは、MeshContainerが保持するのではなく、一段外に追いやってみる。
    • 自分では FRAMEの中に追い出しました。MeshContainerと並列して存在する感じに。

このような点から、
描画用のオブジェクト生成時には、

  • FRAME構造をコピー(別インスタンス作成)
  • AnimationControllerをcloneする
  • 上記でそれぞれ描画用のインスタンス用としてのデータができたので、関連づける.
    • D3DXFrameRegisterNamedMatrices()を使用する

これで画像のようにインスタンスごとにアニメーションデータを持った感じで
描画ができるようになりました。

面倒といえば面倒なので、要望があったらlib化してしまうかも。


DirectXのテクスチャについて

メモリ管理やUSAGEの組み合わせで気になったので調査.

生成時フラグ

D3DPOOL D3DUSAGE 結果
Default 0 OK
Default WRITEONLY NG
Default DYNAMIC OK
Managed DYNAMIC NG
SystemMem DYNAMIC OK
Default DYNAMIC RENDERTARGET OK
Default RENDERTARGET OK

ロック時フラグ

タイプ ロック手法 結果
Default&Dynamic&&RenderTarget 0 NG
Default&RenderTarget 0 NG
Default&RenderTarget D3DLOCK_READONLY NG
Default&Dynamic&&RenderTarget D3DLOCK_DISCARD NG
Default&RenderTarget D3DLOCK_DISCARD NG
Default&Dynamic D3DLOCK_READONLY OK
Default&Dynamic D3DLOCK_DISCARD OK
SystemMem&Dynamic D3DLOCK_READONLY OK
SystemMem&Dynamic D3DLOCK_DISCARD OK

感想

意外だったのは SystemMemでDynamicタイプを作れること。

それでもSystemMemでRenderTargetは無理でしたが(当たり前)。


インターフェース変更

DirectXのヘッダを見ていて気付いたこと。
同名のIIDで、値だけ違うってまずいんじゃないだろうか。

DEFINE_GUID(IID_ID3DXEffect,
0xd165ccb1, 0x62b0, 0x4a33, 0xb3, 0xfa, 0xa9, 0x23, 0x0, 0x30, 0x5a, 0x11);

DEFINE_GUID(IID_ID3DXEffect,
0xf6ceb4b3, 0x4e4c, 0x40dd, 0xb8, 0x83, 0x8d, 0x8d, 0xe5, 0xea, 0xc, 0xd5);

確かにメソッドの数が違うから新しいIIDにするのは納得がいくのだけども、こんなことして、平気なのかな?と思う。もしや、こういう強引なことを出来るようになったのも、対応するd3dx_**.dllが分かっているから、なのか?

だれか情報知っていたら教えて欲しいです。

 


D3DXCreateEffectについて – CompileEffect

マクロで有効・無効のブロックを切り替えるにはどうしたらいいんだーということで調べてました。
ピクセルシェーダーコードでは、以下のようにして挙動をチェックしていました。

[code lang=”cpp”]
#ifdef _TEST_
return float4(1,1,1,1);
#else
return float4(0,0,1,1);
#endif
[/code]

これをEffectでやるのにネックだったのは、マクロ定義変数の部分でした。

解決策はこう。

[code lang=”cpp”]
D3DXMACRO d3dxMacros[2] = { //マクロ マクロ名
"_TEST_","", <– ここで定義する部分を設定して
0, 0 <– ここで終端を示すようにヌルターミネートしておく
};
[/code]

通りでマクロの設定長を示す引数がないわけだ。

探してみてもこの辺でつまずく人はいないのか、記述が見あたらず。
また、ほとんどの場合このマクロ設定部分NULLにしちゃうし、使う状況ってないのかなぁ。

自分では多用していこうと思っていますが。


スキニング問題

前からやっていたスキニング、前回までの結果ではうまくできないことが判明し、もがいてます。

出来ないのは下記の理由により。

  •  SkinedMeshサンプルではマトリックスパレットに設定している値そのものがワールドになっている。
  •  結果、ライト位置をモデル空間に変換しただけではNG (全てをワールドでやればOKでしょうが…)

というわけで、スキンメッシュの表示方法そのものを変更しようかと。
点光源にチャレンジしていなかったら、上記の問題点に気付かずに放置していただろうなーと思うと運が良かったのかな。

 


DirectXのロックの謎

DirectX9のLockについて、調べてみたことをメモしておきます。

Textureのロックには、範囲指定があるが、Lockは複数回できるのか?

まずは推測から。
範囲指定がわざわざあるということは、部分1でLockし、部分2でLockしても問題なさそうである。また、領域が重なった場合は多重ロックになりNGとなる。と予想。

結果は・・・領域が被っていようが、別部分であろうがNGでした。どうやら、範囲指定はパフォーマンスをあげるためのダーティー判断くらいでしかなさそう。ただし、異なるミップマップについては同時Lockは可能のようです。

 

VertexBufferのロックはどうだろうか?

テクスチャが上記のような結果になったのだから、やはり多重ロックは失敗するだろうと予想。あるいはNO_OVERWRITEフラグなんかあるし、領域さえ違えば成功するのかも知れない。

結果は・・・領域が被っていようが、別であろうが、多重ロックは成功する。多重ロック後は、正しくUnlock呼ばないといけないそうです。

このロックの挙動の違いは問題になりそう…。そもそも多重ロックを許可しない方針でプログラムを作っていくべきかも知れません。

上記を試したのは、DirectX9.0cで、GeForce7600GT on Vista(x64)です。他のメーカーのカードでは異なる結果となるかも知れません。 次は、OpenGLのMapBufferとか調べてみる?!

 


XSI ModToolからスキニングメッシュ出力

スキニングメッシュとして、円柱にボーンを仕込んだものを作ってみました。

適当に曲げてみて、それを独自形式変換して表示できるかやってみたのですが、失敗してしまい、 tiny.xからの作った場合には正常に表示できているのですが。

うまく出す方法判明

いくつか試してようやく解法がわかりました。
DirectXのエクスポーターの設定で CompressMeshにチェックが入っているとダメのようです。

また、Trianglateにチェックを入れても豪快に崩れました。
このことから、素直に .xを作り、やるならその後最適化をするという流れをとった方が良さそうです。

 


スキニングメッシュ

ようやくtiny.xを自前フォーマットに変換して描画まで確認。
苦労したけど、サンプルのSkinnedMeshのデータと比較できたのでまだ楽だった。

スキニングの一番の問題点は、行列のデータを見ても全く正しいことがわからないこと。オフセット行列も正しいか謎だし。簡単なサンプル作るにしても、スキニングメッシュを作るには、一般的にはモデラーが必要になるし。

今回の一番のポイントは、DrawSubsetらから抜け出せたこと。

drawSubsetの中身は結局は描画バッチを実行しているだけに過ぎない。
内部では、DrawPrimitive呼び出しを実行しているだけだろうし、その部分を自分で何とかやろうとしてみた。

ConvertToIndexedBlendedMesh を実行すると、属性グループ、ボーンコンビネーションバッファが取得できるのでこれから何とでもなる。

pBoneComb[ AttibId ]で、1回のDrawPrimitive呼び出しに必要な頂点数、インデックスなどは入っているし、

必要とするボーン(フレーム)へのIDも取得できる

pBoneComb[ AttribId ].BoneId[ n ]

ここで、必要とするボーン数はパレットの数としてメッシュコンテナが取得しているので、そのループで回せば必要とするボーンIdが取れる。

このボーンIdから、さらにGetBoneName( BoneId )すれば対象とするフレームの名前が取れる。

注意点としては、パレット数(nNumPaletteEntries)はシェーダーのマトリックスパレット数で制限される。で、複数回に分けられた後、最後の描画グループでは、パレットが全て埋まるわけではない。

tinyでやったところ、13という中途半端なものになった。
このとき、BoneIdが UINT_MAXになるのでその場合は取得しないなどの条件が必要となる。

この辺はSkinnedMeshのサンプルで実装されていて、あぁ、なるほどと思ったポイント。


スキンメッシュ行列

リフレクションlibにバグが潜んでいた

抽象クラスはNG. templateのインスタンス化後、メンバ取得のために抽象クラスのインスタンス作ろうとするのでNG
ユーザーのオブジェクト型をさらに派生したクラスでのエラー

どうやら、class Aを継承したclass B, class Cが居て、それぞれの初期化と、A自身の初期化で、おかしな状況になっていた。こちらは修正済み。

しかし、最初の項目はシステム上どうしても無理だ…。
抽象クラスのメンバのシリアライズは派生先で再定義してもらうしかないのか…。システム的に破綻っぽい orz

スキンメッシュ

とある場所で、ボーンの部分で、ローカルのワールドの逆行列という記述をみた。
さっぱりなので、色々と調べてみた結果このような表記はXSIの部分で使われているようだ。この行列Inverted_matBasePoseとかOffset行列とかいろいろな呼び方をされている模様。

モデルが初期ポーズをとっている状態でボーン空間をきちんと合わせるのに必要な行列ということらしい。

各ノードのワールドマトリックスを作るには、


ここで逆変換を作用させて、ボーンのローカル空間へ. 骨のポーズ状態が原点としているらしい…

“日々精進時々堕落”というWebが参考になりました。
各状態が算出された後の実装方法は、DirectXのSkinnedMeshサンプルで何とかなるんですが、それに必要なデータの情報ってなかなかないですよね。

 


(no title)

[プログラミング] スキンメッシュ注意点

スキニングメッシュを読み込むときには、

  • D3DXMESHCONTAINER
  • D3DXFRAME
  • ID3DXAllocateHierarchy

らを派生し、ロード&生成コードを自前でかかなくてはいけない。

ここで、派生の決まり事である、デストラクタはvirtualを付けよ。
という点を無意識のうちに書いていて、ミスを犯した。

ルートとなるD3DXLoadMeshHierarchyFromXでの結果格納のpFrameで、
ウォッチで値を見てみると数バイトずれて結果が表示されていた。
そこで疑いを持った。

  • 呼び出し規約でも間違えた?
  • virtualが悪い?

のどちらかがまず怪しい感じがしたので見てみた結果比較的短時間でわかった。

あと気になったのは、ボーンのオフセット行列。
メッシュコンテナに入っていて描画時はそこまで違和感を感じないのだが、
なぜボーン(Frame)の個々の要素として持たないんだろうと。

モデラーから出力されたデータを想定すると、各ボーン毎にオフセット行列があっても良い気がする。