言うことは、このローカル位置に対して親の位置を引き算すれば親ベースになります。親のローカル姿勢の逆行列はこの引き算の作用をします。ローカル姿勢の逆行列は...ボーンオフセット行列です。[親のオフセット行列]なんです。初期姿勢を親の姿勢からの相対姿勢に直します。親子関係を辿るので再起関数が必要です。);上の太文字部分が変換箇所です。引数で入ってくるボーンの初期姿勢はローカルベースなので、親のボーンオフセット行列を右から掛けて親空間ベースに変換しています。この再帰関数はここでしか使わないものなので、ローカルな構造体のメンバ関数にしてしまいましたが、もちろんグローバルな関数でも何らかのクラスのメンバ関数でも構いません。連結の作業が終わりました。ボーンを動かす頂点シェーダここからは「描画の仕方」に話を移します。DirectX9の固定機能パイプラインにはスキンメッシュを行う仕組みが整っています。...かなりにブラックボックスですし、裏でやっている事が見えない分個人的には使いにくさが否めません。今回完全ホワイトボックスなスキンメッシュアニメーションをするために、裏でしていることも全部オープンにします。そのため、描画はすべてシェーダに置き換えます。DirectX9のシェーダには頂点シェーダとピクセルシェーダがあります。この中で頂点を直接動かすスキンメッシュに関与するのは頂点シェーダです。";シェーダは文字列で記述しています。外部のファイルから読み込んでも良いのですが、コンパイル前に文字列化はしますので、最初からプログラム内に埋め込むことも当然できるわけです。頭から見ていきましょう。float4x4型として外部から行列を渡します。view、projはそれぞれビュー行列と射影変換行列で、これはおなじみだろうと思います。特徴的なのはworld[12]とワールド変換行列が12個配列として渡されている部分かなと思います。ここには頂点を動かすための行列の配列をごそっと渡します。12個なのか?これについてはもう少し先まで読んだ段階で説明します。ところで変数の後に「register(c0)」みたいのがありますね。見慣れない方もいらっしゃるかもしれませんが、これは「レジスタ指定」をしています。シェーダはレジスタというGPU内のメモリに格納された値を使って計算を行います。レジスタへはDirectX側から書き込みできるのですが、何も指標がないとDirectX側でどこに何を書けば良いかわからなくなります。そのため、シェーダ側でregister(c0)などのように格納する位置を指定してあげるわけです。続いて頂点シェーダの入力セマンティクス(入力引数)です。上のプログラムではVS_OUTという構造体にまとめています。posには頂点のローカル座標、blendにはその頂点に影響を与えるボーン行列の影響力、そしてidxには影響を与えるボーンの番号がそれぞれ描画デバイスから渡されてきます。これは「�Bで頂点宣言」で指定した要素と型も含め全く一緒にする必要があります。出力は変換後の頂点座標だけです。頂点シェーダ本文(main関数)を見てみましょう。);ここでは1つの頂点を動かす合成行列を作っています。作り方はとっても簡単で、影響するボーン行列を重み付けして足し算しているだけです。式で表すと次の通りです:1つの頂点が複数の変換行列によってちょこっとずつ動かされるからスキンメッシュになるわけでして、上の計算は極めてコアで重要な部分と言えます。すべての頂点が必ず4つのボーンに影響されているわけではありません。頂点シェーダ内で影響数を分岐構文で書くとパフォーマンスを落とします。よって、影響していない部分は影響力wを0にしてしまう事で対処します。後はいつものようにローカル頂点に対して[ワールド]×[ビュー]×[射影変換]と行列をかけ算して頂点を射影空間に移動させて出力します。1.0f);""}";結局スキンメッシュアニメーションの本質は頂点シェーダでして、ピクセルシェーダは必要に応じで好きなように描画してくれればいい部分です。�Fのシェーダプログラムは言ってみれば人が読める文字列(HLSL)でして、このままではDirectXは理解してくれません。よって、このプログラムをコンパイルしてシェーダオブジェクト(IDirect3DVertexShader9、IDirect3DPixelShader9)にする必要があります。);shader->Release();シェーダプログラムをD3DXCompileShader関数を用いてコンパイルしDirectXが理解できるデータの塊にします(ここは流石にブラックボックスです)。そこからIDirect3DDevice9::CreateVertexShader及びCreatePixelShaderメソッドに渡すとIDirect3DVertexShader9オブジェクトとIDirect3DPixelShader9オブジェクトを作ってくれます。HLSLに頂点シェーダとピクセルシェーダの両方を書く事が多いかと思いますが、IDirect3DDevice9が扱えるのは個別のシェーダプログラムなのでご注意ください。上のようにコンパイルに失敗したかを判定するようにするとデバッグウィンドウにコンパイルエラーになった部分を出力してくれます。ボーンを動かそう!さて、ここまで揃うとスキンメッシュをするための準備が整います。合成行列を計算する頂点シェーダ、ピクセルシェーダ案外こんなもんです(^-^)。これだけ揃えてボーンを動かすと、メッシュが勝手に付いてきてくれます。(共に親空間ベース)です。);実はたったのこれだけです。defBoneという配列は各ジョイントの自分自身の軸ベースの動きです。�Eの中で出てきた「自分の空間での差分姿勢」です。これはどう動かしても良いわけですが、今回は自分のY軸を回転軸として±70度回ってもらうことにしました(ルートボーンである0番は動かないとしました)。次にその差分行列と各ジョイントの初期姿勢から親空間での姿勢を計算しています。これも�Eですでに説明した通りです。次からが一番の肝!「ジョイントを動かす」と言うのはワールド空間での各ジョイントの姿勢を計算することです。[モデルのワールド空間での姿勢]と分解できます。[親のローカル空間での姿勢]で表現されます。右辺の[自分の親空間ベースでの姿勢]はついさっき計算して全部揃えました。よって後は親のローカル空間での姿勢があれば良いわけです。[親の親のローカル空間での姿勢])と右辺第2項をさらに分解すると答えが出てきます。同様の分解を一番上の親まで続けると、各ボーンの親空間ベースでの姿勢行列をズラッと掛け算したものが出てくるわけです。さっき計算した行列ですべて計算できてしまうわけです。必要なのは「ジョイントの一番祖先までつながる姿勢行列の列」です。上のプログラムのUpdateBoneは、再帰関数を用いて各ジョイントのローカル空間での姿勢、さらに合成行列を計算しています。引数に渡されるBoneは自分のワールド空間での姿勢を計算します。計算後その行列を自分の子に渡します。子は上から降ってくる親の姿勢を用いて自分の姿勢を更新します。これを末端まで続けるわけです。自分の兄弟に対しては親の行列を引渡しています。スキンメッシュアニメーション(FK)をする場合、たぶん必ずこの再帰関数が出てくると思います。再帰関数内で合成行列も一緒に計算しています。[自身のワールド姿勢]となっています。頂点をボーンオフセット行列で原点中心に戻し、それをワールド空間に一気に飛ばしているわけです。[モデルのワールド変換行列]と分解して考えると見通しが良くなるのではないでしょうか。変数を書き込むレジスタ位置はシェーダに書いてありますよ。sizeof(Vertex));描画デバイスには頂点シェーダとピクセルシェーダの両方を渡します。頂点シェーダに記述してあるレジスタに合わせてビュー行列や射影変換行列も渡します(SetVertexShaderConstantFメソッド)。レジスタ8番以降は合成行列の配列をごそっと渡します。�Dの最後に出てきたcombMatがここでようやく再出しました。すべてはこの行列の配列を作るために仕込んできたんです。ふ〜(^-^;今回はポリゴンをワイヤーフレームで描画しますのでSetRenderStateメソッドのD3DRS_FILLMODEをD3DFILL_WIREFRAMEに設定します。裏から見ても描画して欲しいのでカリングは切りました。最後に頂点宣言を描画デバイスに教えて、実際のデータをDrawPrimitiveUPメソッドで渡すことで描画が完了します。あ、もちろんちゃんと頂点バッファ(IDirect3DVertexBuffer9)を作って描画してもいいんですよ(むしろそっちの方が超高速なのでお勧め)。して合成行列の配列の要素が12個なのか?スキンメッシュの基本的な描画については以上ですべてです。ところでそう言えば、頂点シェーダに定義した合成行列の配列の要素数が12個だったのを覚えておりますでしょうか?これどうして12個なのか?1枚の三角ポリゴンに影響する最大のジョイント(ボーン)数はいくつでしょうか。頂点数が3つで1つの頂点に影響するジョイント数は最大4つ。各頂点が全く別のジョイントから影響されるとすると3×4=12というわけです。「なんだそれだけ...」と思ったら大間違い。これは重要な問題なんです。合成行列の要素数が12と言うことは、1つのメッシュに使えるボーンの数は最大12本という事になってしまうんです。これは人体など四肢を持つ動物などでは全く仕様に耐えません。一つ考えられる解決方法は頂点シェーダの合成行列の要素数を増やすことです。これならばかなりのモデルに対応できます。これには「定数レジスタ数の壁」が立ち塞がります。vs_3_0」によると、定数レジスタの数はビデオカード依存で、少なくとも256個と書かれています。1レジスタはfloat4つ分です。行列はfloat16個なので、1行列につき4つレジスタを消費する事になります。12個渡すと実は48個も消費していたわけです。これが100行列だと400レジスタ。理論上は最大64個しか頂点シェーダに渡せないんです。最適化するならば、行列を4×3の形で渡します。1)なのでシェーダ内でくっつけてあげるわけです。すると3レジスタしか消費しないので85個行列を渡せます。ジョイントが100以上あるモデルには対応できません。「そんなモデル早々無いでしょ?」。いやいや、最近のハイエンドなゲームだと100どころか200を超えるメッシュもざらです。1メッシュではいずれ限界が来るわけです。するか?メッシュを分割します。分割するかと言うと頂点シェーダ内に引き渡す合成行列数を超えないポリゴングループに分割するんです。これは中々面倒な作業ですが、DirectXのスキンメッシュはその辺をやってくれています(ここはありがたいもんです)。これについてはそのうちマルペケでも取り扱うことになるかなぁと予想しています。オリジナルフォーマットを作ってみようじゃないかDirectX10以降ではXファイルのサポートがバッサリ切られています。Format参照)、どうも本流になっていない感じですし、そもそも出力できるモデラーが皆無と思われます。DirectX10が今一盛り上がらなかった理由の一つはメッシュが簡単に出せない事にあったかなと思います。これはDirectX11でも同様で、メッシュが出せないとせっかくの高機能も台無しです。Xファイルのような広く知れ渡るフォーマットとライブラリの出現を待つか、自分でさっさと作ってしまうかです。本格大規模ゲームを作ろうと思うなら、ぜひ後者を選択して欲しいと思います。マルペケでもこの辺のオリジナルフォーマットについては検討しております(2010.2現在)。この章ではスキンメッシュアニメーションについて長々と説明してきました。スキンメッシュアニメーションをするのに必要とする物はそれほど多くはありません。この章は上からどどっと実行しているだけですが、より発展させるならばこの処理をちゃんとクラス化する必要があります。それについては...いずれまたでしょうか(^-^;。