ページ 11

DirectXの板ポリゴンの回転のさせかたについて

Posted: 2010年3月09日(火) 23:11
by 天空橋
はじめまして、天空橋と申します。

DirectXの板ポリゴンの回転のさせかたについて質問があります。

2DシューティングゲームをVisual C++2008とDirectXにて作成しようとしています。
そこで、テクスチャを読み込んで、板状のポリゴンに貼り付けて、表示することはできました。
次に、2D描画のスーパーファミコン的な基本として拡大、縮小、回転機能を実装しようとしたのですが、
拡大、縮小は出来たのですが、どうにも、回転だけ巧くできませんでした。

ソースは、以下のようになっています。

///////////////////////////////// ここから

D3DXMATRIX rot;
D3DXMatrixRotationZ(&rot, roll); //回転

// モデル座標
D3DXVECTOR4 vect[4];
vect[0].x = -1; vect[0].y = -1; vect[0].z = 0;
vect[1].x = 1; vect[1].y = -1; vect[1].z = 0;
vect[2].x = -1; vect[2].y = 1; vect[2].z = 0;
vect[3].x = 1; vect[3].y = 1; vect[3].z = 0;

// 回転用の行列を積算
for (int i = 0; i < 4; i++) {
vect.x = (vect.x * rot._11) + (vect.y * rot._21) + (vect.z * rot._31) + rot._41;
vect.y = (vect.x * rot._12) + (vect.y * rot._22) + (vect.z * rot._32) + rot._42;
vect.z = (vect.x * rot._13) + (vect[i].y * rot._23) + (vect[i].z * rot._33) + rot._43;
vect[i].w = (vect[i].x * rot._14) + (vect[i].y * rot._24) + (vect[i].z * rot._34) + rot._44;
}

// 書き込む頂点情報の作成
Vertex v[6] = {
{vect[0].x, vect[0].y, vect[0].z, D3DCOLOR_ARGB(255, 255, 255, 255), 0, 0},
{vect[1].x, vect[1].y, vect[1].z, D3DCOLOR_ARGB(255, 255, 255, 255), 1, 0},
{vect[2].x, vect[2].y, vect[2].z, D3DCOLOR_ARGB(255, 255, 255, 255), 0, 1},
{vect[3].x, vect[3].y, vect[3].z, D3DCOLOR_ARGB(255, 255, 255, 255), 1, 1},
{vect[2].x, vect[2].y, vect[2].z, D3DCOLOR_ARGB(255, 255, 255, 255), 0, 1},
{vect[1].x, vect[1].y, vect[1].z, D3DCOLOR_ARGB(255, 255, 255, 255), 1, 0},
};

pD3DDevice->SetTexture(0, texture);
pD3DDevice->SetFVF(D3DFVF_VERTEX);
pD3DDevice->SetRenderState(D3DRS_LIGHTING, FALSE);

// World 座標の設定
D3DXMatrixIdentity(&matWorld);
pD3DDevice->SetTransform(D3DTS_WORLD, &matWorld);

//View 座標の設定
D3DXMatrixIdentity(&matView);
D3DXMatrixLookAtLH(&matView, &D3DXVECTOR3(0, 0, 30), &D3DXVECTOR3(0, 0, 0), &D3DXVECTOR3(0, -1, 0));
pD3DDevice->SetTransform(D3DTS_VIEW, &matView);

//透視変換の設定
D3DXMatrixIdentity(&matProj);
D3DXMatrixPerspectiveFovLH(&matProj,D3DX_PI / 4, 800.0f / 600.0f, 0.0f, 100.0f);
pD3DDevice->SetTransform(D3DTS_PROJECTION, &matProj);

pD3DDevice->DrawPrimitiveUP(D3DPT_TRIANGLELIST, 2, v, sizeof(Vertex));

///////////////////////////////// ここまで


なお、上記のソースは、World座標を自前で、計算しているつもりです。
DirectXの場合、DrawPrimitiveの回数を極力減らしたほうが良いと本に書いてありました。
そこで、シューティングゲームとして、弾を沢山描画することになるので、描画処理を速くする必要があると思い、
回転させる都度SetTransform関数にてWorld座標を計算すると、その都度、DrawPrimitiveをするため、
遅くなると思い、上記のようにしているつもりです。

なお、上記の回転処理をSetTransformで計算すると期待通りにきれいな回転をします。

より、詳細なソースと、SetTransformを用いて正しく回転するソース添付いたします。

やりたいことは、基本的な描画機能を高速におこなうことです。
なお、C言語は読むのに困らない程度は理解しているが、書くのはあまり得意ではありません。
また、今回のソースは微妙にC++になっていたりします。
DirectXは、ほぼ未経験となります。

皆様、ご教授おねがいいたします。

Re:DirectXの板ポリゴンの回転のさせかたについて

Posted: 2010年3月09日(火) 23:31
by 天空橋
おかしい回転というのがどのようなものか、伝えづらかったので、おかしい回転をしているアプリを添付してみました。

多分、DirectXのランタイムがある環境にて、解凍して、実行すれば動作するかと思います。

皆様よろしくお願いいたします。

Re:DirectXの板ポリゴンの回転のさせかたについて

Posted: 2010年3月10日(水) 00:13
by conio
逆に「上手く行った回転」とはどういうものでしょうか?


「テクスチャの中心座標を中心としたZ軸方向の回転」という事でしょうか?
(観覧車を真正面から見たような回転)


とりあえず、こんな感じでどうでしょうか。
---------------------------------------------------------------
D3DXQUATERNION q(0,0,0,1); // 単位クォータニオン'q'を生成
D3DXVECTOR3 NAxis( 0, 0, 1); // 任意の中心軸
D3DXMATRIX TurnMat; // 回転行列
D3DXMATRIX mat; //平行移動行列
static int d;

//何もしない行列を作る
D3DXMatrixIdentity(&mat);
D3DXMatrixIdentity(&TurnMat);

//板状のポリゴンの中心座標は、(x = 3, y = 3)とする。
D3DXMatrixTranslation(&mat,3,3,0);

//z軸を中心に 0~360度 回転するクォータニオン'q'を生成
D3DXQuaternionRotationAxis( &q, &NAxis, D3DXToRadian(d = (d + 1) % 360));
//クォータニオンを行列に変換。
D3DXMatrixRotationQuaternion(&TurnMat, &q);
//行列をかける(回転させた後、行列'mat'で平行移動させる)
TurnMat*=mat;
pD3DDevice->SetTransform( D3DTS_WORLD, &TurnMat);
---------------------------------------------------------------
上のコードを描画ループの部分に入れれば多分 想定した回転はするんじゃないかな、と。 画像

Re:DirectXの板ポリゴンの回転のさせかたについて

Posted: 2010年3月10日(水) 00:53
by 天空橋
conio さん ご回答ありがとうございます。

「上手くいった回転」とは、conioさんがおっしゃるとおり、
「観覧車を真正面から見たような回転」のことです。
頂いたソースを試したところ、「上手くいった回転」が行えました。

ただ、頂いた実装だと、実際にゲームを作る際に、
角度の異なる画像ごとに、SetTransform(D3DTS_WORLD)関数及び、
DrawPrimitiveUP関数を呼び出すことになると思っています。

たとえば、弾を1度刻みで360度ばら撒く敵がいた場合、
360回のDrawPrimitiveUP関数を呼び出すこととなります。
これを、回避するにはどうにかして、SetTransformを用いずに、
頂点情報を直接World変換する必要があるのだと思っています。
そして、直接World変換した頂点を以下のような形で書き出せば、
360枚の角度の異なる画像を一回のDrawPrimitiveUPで行えるのではと、思っています。

// spriteCountは書き出す画像の数 (今回だと360)
// vには360 * 6 の頂点情報が格納されている
pD3DDevice->DrawPrimitiveUP(D3DPT_TRIANGLELIST, spriteCount * 2, v, sizeof(Vertex));

上記のような、アプローチのときに、画像を回転させる良い方法はないでしょうか?
または、アプローチが既に間違っていたりするのでしょうか?
たとえば、

・実は、DrawPrimitiveUPは板ポリゴンの枚数分行うのが普通
・モデル座標の頂点に自前で回転を行うなんて出来ない

などなど。

Re:DirectXの板ポリゴンの回転のさせかたについて

Posted: 2010年3月10日(水) 01:42
by Justy
 conioさんの方法で行列だけ求めた後、SetTransform()の代わりに
頂点座標を D3DXVec3TransformCoord()とか D3DXVec3TransformCoordArray()で
座標を変換すればいいのでは?

Re:DirectXの板ポリゴンの回転のさせかたについて

Posted: 2010年3月10日(水) 14:13
by conio
>>360枚の角度の異なる画像を一回のDrawPrimitiveUPで行えるのではと、思っています。
これは、出来ます。
その前に、プリミティブタイプはD3DPT_TRIANGLESTRIPの方が良いんじゃないかなと思います。
(D3DPT_TRIANGLELISTだと、頂点が重複して数が多くなってしまうので)

※「離れたポリゴンを描画するには、LISTじゃないと無理」という理由でLISTにされたのであれば、
STRIPに変更した方が良いかと思います。
STRIPでも離れたポリゴンを描画できるやり方があります。


やり方は 1つの配列に全ての板ポリゴンのデータを入れればOKです。
ただし、入れる際は板ポリゴンのデータとデータの間に 2つの頂点を挟みます。
(縮退三角形により、離れた板ポリゴン同士を繋がせるため)

2つの頂点は、
---------------------------------
1番目が、直前の板ポリゴンの右下
2番目が、直後の板ポリゴンの左上
---------------------------------
となります。

コードにすると下記の様な感じです。
--------------------------------------------------------------------
#define MY_VERTEX_FVF (D3DFVF_XYZ | D3DFVF_DIFFUSE)
struct MY_VERTEX{
D3DXVECTOR3 p;
DWORD color;
};
MY_VERTEX mv[/url] = { { D3DXVECTOR3(-1, 1, 0), 0xffff0000}, //板ポリゴンA 左上
{ D3DXVECTOR3( 1, 1, 0), 0xff00ff00}, //板ポリゴンA 右上
{ D3DXVECTOR3(-1,-1, 0), 0xff0000ff}, //板ポリゴンA 左下
{ D3DXVECTOR3( 1,-1, 0), 0xffffffff}, //板ポリゴンA 右下

{ D3DXVECTOR3( 1,-1, 0), 0xffffffff}, //板ポリゴンA 右下
{ D3DXVECTOR3(-1, 1, 0), 0xffff0000}, //板ポリゴンB 左上

{ D3DXVECTOR3(-1, 1, 0), 0xffff0000}, //板ポリゴンB 左上
{ D3DXVECTOR3( 1, 1, 0), 0xff00ff00}, //板ポリゴンB 右上
{ D3DXVECTOR3(-1,-1, 0), 0xff0000ff}, //板ポリゴンB 左下
{ D3DXVECTOR3( 1,-1, 0), 0xffffffff}, //板ポリゴンB 右下
};
--------------------------------------------------------------------


それと、下のが
別々に動かすために作った まぁ適当なコードです。
-----------------------------------------------------------
pD3DDevice->SetTransform(D3DTS_WORLD, &mat);
pD3DDevice->SetFVF(MY_VERTEX_FVF);

static float x2 = 1.0f/1000;
mv[0].p.x += x2;
mv[1].p.x += x2;
mv[2].p.x += x2;
mv[3].p.x += x2;
mv[4].p.x += x2;

mv[5].p.x += -x2;
mv[6].p.x += -x2;
mv[7].p.x += -x2;
mv[8].p.x += -x2;
mv[9].p.x += -x2;

pD3DDevice->DrawPrimitiveUP(D3DPT_TRIANGLESTRIP, 8, mv, sizeof(MY_VERTEX));
-----------------------------------------------------------

※縮退三角形を一回用いるたびに、三角ポリゴンの数が4つ増えます。


その為、
-----------------------------------------------------------------------
板ポリゴンA(2つ) + 板ポリゴンB(2つ) + 縮退三角形の分(4つ)
-----------------------------------------------------------------------
の、8を三角ポリゴンの数として指定しています。


最後に。
テクスチャーが異なる場合は、 描画処理を分ける必要があります。
一度に描画している以上、SetTexture関数を呼ぶと全ての板ポリゴンに適用される為です。

なので、下記の様にテクスチャごとに一次元配列を用意し、描画する必要があります。
(DrawPrimitiveUP関数の呼び出しの数 = テクスチャの種類の数)
-----------------------------------------------------------------------------
pD3DDevice->SetTexture(省略);
pD3DDevice->DrawPrimitiveUP(D3DPT_TRIANGLESTRIP, 8, mv, sizeof(MY_VERTEX2)); //テクスチャAの板ポリゴン全て
pD3DDevice->SetTexture(省略);
pD3DDevice->DrawPrimitiveUP(D3DPT_TRIANGLESTRIP, 8, mv2, sizeof(MY_VERTEX2)); //テクスチャBの板ポリゴン全て
----------------------------------------------------------------------------- 画像

Re:DirectXの板ポリゴンの回転のさせかたについて

Posted: 2010年3月10日(水) 23:32
by 天空橋
>> Justy さん

ご回答ありがとうございます。
ご指摘の方法で、無事やりたい回転を行うことが出来ました。

ちなみに、私の行列計算だと、「w=1に射影する」計算が完全に抜けていたのですね。

でも、この間違いのために、一週間近く悩んでいましたw
本当にありがとうございました。

>> conio さん

丁寧な解説ありがとうございます。

> ※「離れたポリゴンを描画するには、LISTじゃないと無理」

これは、まさに、そのとおりです。
離れたところのポリゴンを描画するにはLISTじゃないとだめだと思って、LISTで実装しました。
実は、最初は頂点数が減るだろうと、安易にSTRIPで実装したところ、
板と板との間に微妙な三角形が表示されて、LISTにしたという、経緯がありました。

ということから、conioさんが書かれた、縮退三角形を使うことによって、
STRIPで表示することができるというのは、目から鱗の大発明でした!
HPの方も拝見させていただき、より詳細かつわかりやすく、書かれているのに感動しました!

早速実装してみたところ、STRIPを使って一括で板ポリゴンを描画してみたところ、
きれいに描画することができました。

> テクスチャーが異なる場合は、 描画処理を分ける必要があります。

こちらは、なんとなく、気づいていました。
そのため、できる限りテクスチャを変えないように、一つのテクスチャに複数の画像を含め、
UV座標を変更することによって対応しようと考えています。

色々と、教えていただきありがとうございます。

本件、クローズしたいと思います。

Re:DirectXの板ポリゴンの回転のさせかたについて

Posted: 2010年3月10日(水) 23:33
by 天空橋
すいません。解決をチェックし忘れてしまいました。