前提・実現したいこと
今回DirectX9でFbxモデルを使ったスキンメッシュアニメーションに
挑戦しています。
CPU側でのスキニングは行えたのですが、FPSが想像以上に重く、
どうしようかと調べていたら、
GPU側でスキニングできると知りました。
なのでシェーダーを使って頂点ブレンドということをしようとしています。
D3DXMATRIX Bone[64]; ボーン行列ですこの中に頂点ブレンドで必要なボーンを入れていきます。
ボーン行列をID3DXEffect->SetMatrixArray(bone*)で
ボーン行列配列のポインタを送っています。
bone_index[4]に影響するボーンを代入しています。
weightには影響する度数。
発生している問題・エラーメッセージ
GPU側でのスキニングができません。
GPUへ送り込むパラメータがいけないのか、
計算式が悪いのか、アニメーションが行われないです。
自分の見方としては計算式は合っていると思うのですが、
他の方からみればどうでしょう?
該当のソースコード
C++
1 2VertexBlend.fx... 3 4// グローバル定数 5// ビュー行列 6float4x4 g_view_mat : VIEW; 7 8// 射影行列 9float4x4 g_proj_mat : PROJECTION; 10 11// ワールド行列 12float4x4 g_world_mat : WORLD; 13 14// ボーン姿勢マトリックス 15float4x4 g_bone_mat[64]; 16 17// ブレンドする配列の数 18int g_max_blend_idx; 19 20// 使用するテクスチャ 21texture g_tex; 22 23// サンプラブロック 24sampler smp = sampler_state { 25 texture = <g_tex>; 26}; 27 28// 頂点シェーダー 29void VS( 30 float4 pos : POSITION, 31 float4 blend_w : BLENDWEIGHT, 32 int4 idx : BLENDINDICES, 33 float3 normal : NORMAL, 34 float4 color : COLOR0, 35 float2 uv : TEXCOORD0, 36 out float4 get_pos : POSITION, 37 out float4 get_color : COLOR0, 38 out float2 get_uv : TEXCOORD0 39) 40{ 41 // vs_out 42 float4 out_pos = (float4)0.f; 43 44 45 // 射影変換用 46 float4 proj_pos; 47 48 // スキニング 49 { 50 // 単位行列化 51 float4x4 skin_transform = float4x4( 52 1.0f, 0.0f, 0.0f, 0.0f, 53 0.0f, 1.0f, 0.0f, 0.0f, 54 0.0f, 0.0f, 1.0f, 0.0f, 55 0.0f, 0.0f, 0.0f, 1.0f); 56 57 // 使わないidxはblend_wを0にしているので加算されないはず 58 // 配列にidxを入れたら行列計算がおかしくなる 59 skin_transform += g_bone_mat[idx[0]]*blend_w.x; 60 skin_transform += g_bone_mat[idx[1]]*blend_w.y; 61 skin_transform += g_bone_mat[idx[2]]*blend_w.z; 62 skin_transform += g_bone_mat[idx[3]]*blend_w.w; 63 // 各行列計算 64 float4 trans_pos = mul(pos, skin_transform); 65 float4 world_pos = mul(trans_pos, g_world_mat); 66 float4 view_pos = mul(world_pos, g_view_mat); 67 proj_pos = mul(view_pos, g_proj_mat); 68 } 69 70 // 外へ出力 71 get_color = color; 72 get_uv = uv; 73 get_pos = proj_pos; 74 75} 76 77 78// ピクセルシェーダ 79float4 PS( 80 float4 color : COLOR0, 81 float2 uv : TEXCOORD0 82) : COLOR0 83{ 84 float4 out_put; 85 86 // テクスチャ座標 87 out_put = tex2D(smp,uv); 88 89 // カラー値掛け算 90 out_put *= color; 91 92 return out_put; 93} 94 95 96// テクニック 97technique tech1{ 98 99 pass P0 100 { 101 VertexShader = compile vs_2_0 VS(); 102 PixelShader = compile ps_2_0 PS(); 103 } 104} 105
C++
1 2ShaderCreator.cpp... 3 4 // 頂点要素定義 5 D3DVERTEXELEMENT9 elem[] = 6 { 7 // 位置 8 { 9 // ストリーム番号 10 0, 11 // オフセット 頂点数 * サイズ 12 0, 13 // 変数の型 14 D3DDECLTYPE_FLOAT4, 15 // ポリゴン分割 16 D3DDECLMETHOD_DEFAULT, 17 // セマンティクス 18 D3DDECLUSAGE_POSITION, 19 0 20 }, 21 22 23 // 法線 24 { 25 // ストリーム番号 26 0, 27 // float 4バイト * 4 = 16+ 28 16, 29 // 変数の型 30 D3DDECLTYPE_FLOAT3, 31 // ポリゴン分割 32 D3DDECLMETHOD_DEFAULT, 33 // セマンティクス 34 D3DDECLUSAGE_NORMAL, 35 0 36 }, 37 38 // カラー 39 { 40 // ストリーム番号 41 0, 42 // float 4バイト * 3 = 12+ 43 28, 44 // カラー(UBYTE4) 45 D3DDECLTYPE_D3DCOLOR, 46 // ポリゴン分割 47 D3DDECLMETHOD_DEFAULT, 48 // セマンティクス 49 D3DDECLUSAGE_COLOR, 50 0 51 }, 52 53 // uv 54 { 55 // ストリーム番号 56 0, 57 // DWORD 4+ 58 32, 59 // 変数の型 60 D3DDECLTYPE_FLOAT2, 61 // ポリゴン分割 62 D3DDECLMETHOD_DEFAULT, 63 // セマンティクス 64 D3DDECLUSAGE_TEXCOORD, 65 0 66 }, 67 68 69 70 // 重み 71 { 72 // ストリーム番号 73 0, 74 // float 4b * 2 = 8+ 75 40, 76 // 変数の型 77 D3DDECLTYPE_UBYTE4, 78 // ポリゴン分割 79 D3DDECLMETHOD_DEFAULT, 80 // セマンティクス 81 D3DDECLUSAGE_BLENDWEIGHT, 82 0 83 }, 84 85 86 87 // 重みインデックス 88 { 89 // ストリーム番号 90 0, 91 // 4+ 92 44, 93 // 変数の型 94 D3DDECLTYPE_UBYTE4, 95 // ポリゴン分割 96 D3DDECLMETHOD_DEFAULT, 97 // セマンティクス 98 D3DDECLUSAGE_BLENDINDICES, 99 0 100 }, 101 102 103 104 // 終了 105 D3DDECL_END() 106 }; 107 108 109 // 頂点データをシェーダー用のデータに変換 110 HRESULT hr = Getdevice()-> 111 CreateVertexDeclaration( 112 elem, 113 &m_p_decl 114 ); 115 116 117 // 読み込めてなかったら 118 if (hr != S_OK) { 119 hr = S_OK; 120 return; 121 } 122
SkinCustomVertex.h... // fvf(多分シェーダーでは使わない) #define FVF_SKIN ( D3DFVF_XYZW /*| D3DFVF_LASTBETA_UBYTE4| D3DFVF_XYZB4*/|D3DFVF_NORMAL | D3DFVF_TEX1 | D3DFVF_DIFFUSE) struct SkinCustomVertex { // コンストラクタあり SkinCustomVertex(); // 頂点データ D3DXVECTOR4 vertex; // 法線ベクトル(現在はなしなので、戻す場合はfbxの処理も戻すようにする) D3DXVECTOR3 normal; // カラー DWORD diffuse; // テクセル D3DXVECTOR2 uv; // スキンメッシュ用重み(この頂点で影響する重みを全て持たせる) D3DXVECTOR4 weight; // スキンメッシュ用影響するボーン番号(頂点) unsigned char bone_index[4]; };
fbx.cpp... void Fbx::EffectDraw( const int&vertex_num, const int&polygon_num, const D3DXMATRIX&world_mat, D3DXMATRIX*bone_mat_list, const int&max_bone_index ) { // ボーン行列セット m_effect.SetWorldMatrix(world_mat); // ボーンセット m_effect.SetBoneMatrix( bone_mat_list ); // ボーン配列数 m_effect.SetBoneCount(max_bone_index); // ボーン最大数 m_effect.SetMaxIndex( max_bone_index ); // 更新 m_effect.Update(); // パスの数 UINT pass_num = 0; // シェーダー描画の開始 m_effect.ShaderBegin(pass_num, 0); // パス0の開始 m_effect.BeginPass(0); // 描画 DrawPrimitive(vertex_num, polygon_num); // パスの終了 m_effect.EndPass(); // シェーダーの描画終了 m_effect.ShaderEnd(); }
試したこと
トライ&エラーで色々調べたのですが、
自分の見解ではBLENDINDICESセマンティクスに変数が渡せていない(もしくは他の可能性)のではないのかと思います。
普通にシェーダー側のボーン行列に0番号を入れたら動いたので。
bone_indexが機能していない可能性があります。
他のセマンティクス情報は読み込めてます。(UVとかPOSとか)
なので、もう一度D3DVERTEXELEMENT9らへんの値や型を入れ替えたりしてGPU側にindices情報を送ろうと思ったのですが、できません。
MSDNの説明を見ても、固定機能のスキニング(FVF使用)の説明しかされていなかったです。
ほんとにGPUスキニングの記事が少ないので方法をできれば教えていただけると幸いです。
補足情報(FW/ツールのバージョンなど)
環境 : VS++2017
ゲーム開発アプリ : DirectX9
言語 : C++
シェーダー エフェクトファイル model 2.0
あとできればでいいのですが、シェーダーのデバッグツール
GrapicsAnalaizerの使い方を教えてほしいです。
自分の環境ではなぜか使えません。
DX11でないと使えないとかでしょうか?
あと分かりにくい内容があるなら修正いたします。
あなたの回答
tips
プレビュー