fc2ブログ

「インデックス付き頂点ブレンディング」 をやってみた(DirectX)

インデックス付き頂点ブレンディングをやってみました。
(毎度の如く、記事の最後にソースコードも添付します。)

前回は、インデックス無しで頂点ブレンディングをやったのですが、
そのインデックス無しよりも、インデックス付きの方が便利だと思います。

【参考URL】頂点ブレンディングをやってみた(DirectX)
↑前回やった、頂点ブレンディングについての記事。

で、何が便利なのか?と言うと、
まず、ただの頂点ブレンディングだと 影響度を設定した順に行列が適用されるので、
全く違う行列を使いたい場合は行列をセットし直さないといけないのです。

例えば、下記の様な場合
struct MY_VERTEX{
D3DXVECTOR3 p;
float weight1;
float weight2;
float weight3;
DWORD color;
};

MY_VERTEX v2[] = { {D3DXVECTOR3(-1.0f, 2.0f, 0.0f), 0.2f,0.3f,0.4f, 0xffff0000},
//以下略 };


D3DXMATRIX matA, matB, matC, matD;

//回転、拡大縮小などの 詳しい行列の設定は省略。

g_pD3DDevice->SetTransform(D3DTS_WORLDMATRIX(0),&matA);
g_pD3DDevice->SetTransform(D3DTS_WORLDMATRIX(1),&matB);
g_pD3DDevice->SetTransform(D3DTS_WORLDMATRIX(2),&matC);
g_pD3DDevice->SetTransform(D3DTS_WORLDMATRIX(3),&matD);
頂点は
--------------------------------------------------------
行列A(matA)に 0.2
行列B(matB)に 0.3
行列C(matC)に 0.4
行列D(matD)に 0.1  (1.0f - 0.2f - 0.3f - 0.4f= 0.1f)
--------------------------------------------------------
の影響を受けながら動く事になります。

行列の組み合わせが一緒で、影響度だけが違う場合は、
↓このように一度に描画できるのですが、
g_pD3DDevice->SetTransform(D3DTS_WORLDMATRIX(0),&matA);
g_pD3DDevice->SetTransform(D3DTS_WORLDMATRIX(1),&matB);
g_pD3DDevice->SetTransform(D3DTS_WORLDMATRIX(2),&matC);
g_pD3DDevice->SetTransform(D3DTS_WORLDMATRIX(3),&matD);
g_pD3DDevice->DrawPrimitiveUP(D3DPT_TRIANGLESTRIP, 6, v2, sizeof(MY_VERTEX));


もし、行列の組み合わせが違う場合は、
一回それぞれの行列を別の行列にセットし直さないといけないのです。

[ 行列A 行列B 行列C 行列D ]の組み合わせで動くポリゴンAと、
[ 行列W 行列X 行列Y 行列Z ]の組み合わせで動くポリゴンBがあるとき、

ポリゴンAを描画した後、
D3DTS_WORLDMATRIX(0)~D3DTS_WORLDMATRIX(3)に
行列WXYZを設定しなおし
てポリゴンBを描画する事になります。

g_pD3DDevice->SetTransform(D3DTS_WORLDMATRIX(0),&matA);
g_pD3DDevice->SetTransform(D3DTS_WORLDMATRIX(1),&matB);
g_pD3DDevice->SetTransform(D3DTS_WORLDMATRIX(2),&matC);
g_pD3DDevice->SetTransform(D3DTS_WORLDMATRIX(3),&matD);
g_pD3DDevice->DrawPrimitiveUP(D3DPT_TRIANGLESTRIP, 6, pA, sizeof(MY_VERTEX));

g_pD3DDevice->SetTransform(D3DTS_WORLDMATRIX(0),&matW);
g_pD3DDevice->SetTransform(D3DTS_WORLDMATRIX(1),&matX);
g_pD3DDevice->SetTransform(D3DTS_WORLDMATRIX(2),&matY);
g_pD3DDevice->SetTransform(D3DTS_WORLDMATRIX(3),&matZ);
g_pD3DDevice->DrawPrimitiveUP(D3DPT_TRIANGLESTRIP, 6, pB, sizeof(MY_VERTEX));

【参考画像】
インデックス付き

で、何でこんな面倒な事になっているかと言うと、
セットする行列が0~3までで固定になっているからです。

なので、4種類以上の行列を使う場合は、行列をセットしなおす必要が出てくるのです。

それに対し、インデックス付きの場合だと
0~255までの行列群を番号で指定できるので
行列をセットしなおす必要がありません。


先ほどの例で言うと、
まず使う行列を全て0~255のどれかにセットしておきます。
g_pD3DDevice->SetTransform(D3DTS_WORLDMATRIX(0),&matA);
g_pD3DDevice->SetTransform(D3DTS_WORLDMATRIX(1),&matB);
g_pD3DDevice->SetTransform(D3DTS_WORLDMATRIX(2),&matC);
g_pD3DDevice->SetTransform(D3DTS_WORLDMATRIX(3),&matD);
g_pD3DDevice->SetTransform(D3DTS_WORLDMATRIX(4),&matW);
g_pD3DDevice->SetTransform(D3DTS_WORLDMATRIX(5),&matX);
g_pD3DDevice->SetTransform(D3DTS_WORLDMATRIX(6),&matY);
g_pD3DDevice->SetTransform(D3DTS_WORLDMATRIX(7),&matZ);

そして、ポリゴンAでは「0番~3番を使います」
ポリゴンBでは「4番~7番を使います」と指定してやれば

もう0番~3番の行列セットの中だけでやりくりする必要は無くなるのです。
(いちいち別の行列でセットしなおす必要なし)

【参考URL】インデックス付き頂点ブレンディング

~インデックス付き頂点ブレンディングのやり方~

--------------------------------------------------------------
【1】インデックス付き頂点ブレンディングを有効にする
【2】任意の数の行列 でのブレンディングを有効にする
【3】重みとインデックスを含む構造体を定義
【4】行列をセットする。
【5】SetFVF関数を呼ぶ。
【6】DrawPrimitiveUPで描画。

--------------------------------------------------------------
(他にも描画関数がありますが、今回はDrawPrimitiveUP関数を使います。)


【1】インデックス付き頂点ブレンディングを有効にする
有効にするには、SetRenderState関数で下記のフラグを指定してやればOKです。
g_pD3DDevice->SetRenderState( D3DRS_INDEXEDVERTEXBLENDENABLE, TRUE );


【2】任意の数の行列 でのブレンディングを有効にする
タダの頂点ブレンディングの時と同じように、
いくつの行列で頂点を動かすかを設定します。
g_pD3DDevice->SetRenderState( D3DRS_VERTEXBLEND, D3DVBF_1WEIGHTS );
前回述べたように、
第二引数で指定する値は影響を与える行列の数 - 1になります。
2つの行列を使う予定なので、 D3DVBF_1WEIGHTSを指定してます。)


【3】重みとインデックスを含む構造体を定義
struct MY_VERTEX{
D3DXVECTOR3 p;
float weight1;
DWORD matrixIndices;
DWORD color;
};

タダの頂点ブレンディングと違うところは、
重みの後にDWORD型の変数を持ってきます。
このDWORD型の変数で、使用する行列の番号を指定します。

DWORD型は、32ビット(4バイト)なので、
1バイトずつ使って最大4つの行列の番号を指定します。

【参考画像】
インデックス付き2

画像を見ても分かるように、右からindex0~3と並んでます。
(左からではないので注意!)

なので、
-----------------------------------------------
行列A・・・影響度0.2   行列B・・・影響度0.3
行列C・・・影響度0.4   行列D・・・影響度0.1
-----------------------------------------------
と設定したい時は下記の様になります。


struct MY_VERTEX{
D3DXVECTOR3 p;
float weight1;
float weight2;
float weight3;
DWORD matrixIndices;
DWORD color;
};

MY_VERTEX mv[] = {D3DXVECTOR3(-1.0f,1.0f,0.0f), 0.2f,0.3f,0.4f, 0x03020100, 0xffff0000};

g_pD3DDevice->SetTransform(D3DTS_WORLDMATRIX(0),&matA);
g_pD3DDevice->SetTransform(D3DTS_WORLDMATRIX(1),&matB);
g_pD3DDevice->SetTransform(D3DTS_WORLDMATRIX(2),&matC);
g_pD3DDevice->SetTransform(D3DTS_WORLDMATRIX(3),&matD);

※インデックスを指定する部分は0x03020100と右からの順になってます。


【4】行列をセットする。
既に何回も記述されてますが、行列はD3DTS_WORLDMATRIXマクロを使って指定します。
g_pD3DDevice->SetTransform(D3DTS_WORLDMATRIX(0),&matA);


今まで0~3と順番にセットしてきましたが、別に順番にしなくてもOKです。
いきなり35番とか指定しても問題なし。
(但し、0~255までの間。)


【5】SetFVF関数を呼ぶ。
前述の構造体の場合は、
------------------
・頂点
・重み3つ
・インデックス
・頂点色
------------------
で構成されているので、頂点フォーマットのフラグはこのようになります。↓
#define MY_VERTEX_FVF (D3DFVF_XYZB4 | D3DFVF_DIFFUSE | D3DFVF_LASTBETA_UBYTE4)


タダの頂点ブレンディングと違い、インデックス情報がつくので
D3DFVF_LASTBETA_UBYTE4というフラグが増えてます。

あと頂点ブレンディングの時は、構造体に含まれるweightの数 = フラグで指定する値
だったんですけど、
#define MY_VERTEX_FVF (D3DFVF_XYZB1 | D3DFVF_DIFFUSE)
struct MY_VERTEX{
D3DXVECTOR3 p;
float weight1;
DWORD color;
};

#define MY_VERTEX_FVF (D3DFVF_XYZB2 | D3DFVF_DIFFUSE)
struct MY_VERTEX{
D3DXVECTOR3 p;
float weight1;
float weight2;
DWORD color;
};

インデックス付き頂点ブレンディングの場合は、
構造体のweightの数 + 1 = フラグの値
になるようです。

※さっきweight数が3の構造体の場合のFVFの定義を書きましたが、
フラグの方は D3DFVF_XYZB4 になっているはずです。

【6】DrawPrimitiveUPで描画。
あとは通常通り描画するだけです。
特にコレと言った留意点も無いので省略。

【ソースコード】Sample_IndexedBlending.txt



※環境によっては頂点ブレンディングをサポートしてない場合があるらしいので、
まずは、サポートしているかを調べた方が良さそうです。
調べる方法↓

【参考URL】インデックス付き頂点ブレンディングのサポートの判定
(ページの中間ぐらいに記載されてます。)

テーマ : ゲーム製作 関連 - ジャンル : ゲーム

コメント
コメントの投稿
管理者にだけ表示を許可する