ページ 11

頂点変換(ワールド→スクリーン)について

Posted: 2011年10月06日(木) 11:32
by KUROKUN
現在モデルviewerを作っていて、
xファイルを読み込みスクリーン座標への変換をして各頂点をスクリーンに描画する処理を行っているのですが、
適切な位置に描画する事ができません

適切な位置というのは、スクリーン上のモデル表示位置という事です。
取得している頂点がおかしい。或いは、変換が間違っている。と、思うのですが、自身では行き詰ってしまったので、解答おねがいします。
・簡単な処理の流れ(記事投稿現在)
1.xファイル読み込み 
D3DXLoadMeshHierarchyFromXを使用
FVFの形式は、おそらく( D3DFVF_XYZ | D3DFVF_NORMAL | D3DFVF_DIFFUSE | D3DFVF_TEX1 )
2.メッシュデータから頂点データを取得
読み込んだデータは頂点バッファに格納され、直接触れないのでFVFの構造体(CUSTOM_VERTEX)の配列にコピーする

コード:

struct CUSTOM_VERTEX{	
	D3DXVECTOR3	postion;		// 頂点座標
	D3DXVECTOR3	normal;			// 法線
	DWORD	dwColor;			// 頂点の色
	float	u, v;				// テクスチャ座標
};

コード:

// フレームデータ作成関数(読み込んだフレームのデータを自分で作った構造体(CUSTOM_FRAME)に格納)
	HRESULT CreateFrame(){
		HRESULT hr;
		// 参照用フレームポインタ
		LPD3DXFRAME t_pFrameRoot = m_pFrameRoot;
		// フレーム数カウント変数
		int i = 0;
		// バッファロック用のポインタ
		CUSTOM_VERTEX* pDataV;
		WORD* pDataI;
		while(t_pFrameRoot != NULL){
			CUSTOM_FRAME *pFrame = new CUSTOM_FRAME;
			// 各フレーム(メッシュ)における頂点の数 を 取得
			pFrame->VertexNumber = t_pFrameRoot->pMeshContainer->MeshData.pMesh->GetNumVertices();
			pFrame->PorigonNumber = t_pFrameRoot->pMeshContainer->MeshData.pMesh->GetNumFaces();
			// 各フレーム(メッシュ)頂点データ を 作成
			pFrame->pVertex = CreateVertex( pFrame->VertexNumber );
			// 各フレーム(メッシュ)頂点インデックスデータ を 作成
			WORD *index = new WORD[pFrame->VertexNumber] ;
			pFrame->pIndex = index;
			// 頂点バッファの作成
			IDirect3DVertexBuffer9* pVB;
			hr = (*(this->pD3DDev))->CreateVertexBuffer(
					sizeof(CUSTOM_VERTEX)*pFrame->VertexNumber,
					D3DUSAGE_WRITEONLY,
					FVF_CUSTOM_OPTION,
					D3DPOOL_MANAGED,
					&pVB,
					NULL
					);
			if(FAILED( hr )){
				return E_FAIL; 
			}
			// 頂点バッファの取得
			t_pFrameRoot->pMeshContainer->MeshData.pMesh->GetVertexBuffer( &pVB );
			pFrame->pVertexBuffer = pVB;
			// 頂点バッファロック
			t_pFrameRoot->pMeshContainer->MeshData.pMesh->LockVertexBuffer(D3DLOCK_READONLY,(void**)&pDataV);
			// 頂点の取得
			memcpy(pFrame->pVertex, pDataV, sizeof(CUSTOM_VERTEX)*pFrame->VertexNumber);// 書き込み
			// アンロック
			t_pFrameRoot->pMeshContainer->MeshData.pMesh->UnlockVertexBuffer();
			// インデックスバッファ作成
			IDirect3DIndexBuffer9* pIB;
			(*(this->pD3DDev))->CreateIndexBuffer(
					sizeof(WORD)*pFrame->VertexNumber,
					D3DUSAGE_WRITEONLY,
					D3DFMT_INDEX16,
					D3DPOOL_MANAGED,
					&pIB,
					NULL
			);
			if(FAILED( hr )){
				return E_FAIL; 
			}
			t_pFrameRoot->pMeshContainer->MeshData.pMesh->GetIndexBuffer( &pIB );
			pFrame->pIndexBuffer = pIB;
			// ロック
			t_pFrameRoot->pMeshContainer->MeshData.pMesh->LockIndexBuffer(D3DLOCK_READONLY,(void**)&pDataI);
			// インデックス取得
			memcpy(pFrame->pIndex, pDataI, sizeof(WORD)*pFrame->VertexNumber);// 書き込み
			// アンロック
			t_pFrameRoot->pMeshContainer->MeshData.pMesh->UnlockIndexBuffer();
			// リストにフレームのポインタ を 格納
			Frame_List.push_back( pFrame );
			// 参照フレームを子フレームへ進める
			t_pFrameRoot = t_pFrameRoot->pFrameFirstChild;i++;
		}
		FrameNumber = i;
		return S_OK;
	}
3.頂点の変換と描画
// 全体変換行列をセット
void SetAllMatrix(){
matAll = matView * matProj * matScreen;
}
// スクリーン座標に変換
void VTransWorldToScreen(const D3DXVECTOR3 *inVertex, D3DXVECTOR2 *outVertex){
D3DXVECTOR3 t_Vertex(0,0,0);
D3DXVec3TransformCoord(&t_Vertex, inVertex, &matAll);
t_Vertex.z;
outVertex->x = t_Vertex.x;
outVertex->y = t_Vertex.y;
}
// 描画
void Draw_VertexToScreenPosition( Matrix *t_Mat ){
// 変換後の座標
D3DXVECTOR2 t_Pos;
// 矩形描画インターフェース
Rectangle2D rect(pD3DDev);
int TO_SCREEN_RECT_SIZE = 2;
for(DWORD j = 0; j < FrameNumber ; j++){
for(DWORD i = 0;i < Frame_List[j]->VertexNumber-1; i++){
// 座標をスクリーン座標に変換
t_Mat->VTransWorldToScreen((&Frame_List[j]->pVertex.postion), &t_Pos);
// 矩形の初期化(x座標, y座標, インデックス)
rect.SetPoint(t_Pos.x-TO_SCREEN_RECT_SIZE, t_Pos.y-TO_SCREEN_RECT_SIZE, 0);
rect.SetPoint(t_Pos.x+TO_SCREEN_RECT_SIZE, t_Pos.y-TO_SCREEN_RECT_SIZE, 1);
rect.SetPoint(t_Pos.x-TO_SCREEN_RECT_SIZE, t_Pos.y+TO_SCREEN_RECT_SIZE, 2);
rect.SetPoint(t_Pos.x+TO_SCREEN_RECT_SIZE, t_Pos.y+TO_SCREEN_RECT_SIZE, 3);
// 矩形描画
rect.Draw();
}
}
}
*捕捉
SetAllMatrix()は、Render()関数の中で毎フレーム初期化しています。
頂点を、ウォッチして実行してみたのですが、xファイルの頂点情報と、読み込んだ頂点情報(バッファ格納後)は異なっています。
最適化などが、行われているのでしょうか?
無駄に頂点バッファ等を作成していますが、現在使っておりません。
DrawPrimitive()によりモデルの描画を行おうと、取得したバッファをそのまま入れてみたり、色々試してみたのですが、
ぐちゃぐちゃに表示されてしまうため、DrawSubset()により描画しています。
この問題についても心当たりあれば、解答お願いします。

拙い文章ですがよろしくお願いします。
画像も貼付いたします。画像

Re: 頂点変換(ワールド→スクリーン)について

Posted: 2011年10月06日(木) 18:03
by ISLe
KUROKUN さんが書きました: FVFの形式は、おそらく( D3DFVF_XYZ | D3DFVF_NORMAL | D3DFVF_DIFFUSE | D3DFVF_TEX1 )
メッシュからGetFVFメソッドで取得したフラグと一致してますか?

Re: 頂点変換(ワールド→スクリーン)について

Posted: 2011年10月06日(木) 18:56
by KUROKUN
おそらくと書いた手前、確認していなかったのですが、
確認してみたところ、取得出来たDWORDの値が異なりました。
それによって、データの欠落が起こっているかもしれません。
以前フラグの指定を間違って描画した際、描画時に不正なポインタのアクセスが起こったので修正できましたが、
今回それが起こらなかったため、見逃してしまっていました。

それで、更に質問なのですが、
取得したFVFを基準とした構造体を作りたいのですが、
DirectXのxファイル読み込みで作ったメッシュデータのFVFのフラグは、
デフォルトで初期値は決まっているのでしょうか?
それとも、読み込み時に決定するのでしょうか?
FVF構造体は予め定義しておく必要があるため、データが欠落、破損?しない構造体を用意するには、どのようにすれば良いでしょうか?
こちらでも考えていますが、何か良い方法をご存じでしたらご教授お願いします。
[追記]
ご指摘通り、フラグの設定ミスで、構造体のサイズが異なり、取得時にデータがずれているのが原因のようです。
ただ、単純にsizeof()で取得したFVFの大きさの頂点バッファを作るだけでは、
こちらで作った頂点データの構造体との互換性がありません。
できれば、memcopyは使いたいです。
ですが、DWORDに格納して、上位のビットからfloat[3]の部分を抜き出すのが無難でしょうか?
その場合、取得した頂点を編集して頂点バッファに書きこむ方法が分かりません。

Re: 頂点変換(ワールド→スクリーン)について

Posted: 2011年10月07日(金) 02:04
by ISLe
Xファイルから読み込んだメッシュの頂点フォーマットがいつも同じとは限らないので、描画に使いたい頂点フォーマットに変換する必要があります。
基本的にmemcopyは使えません。

頂点データ一個分のサイズはID3DXBaseMesh::GetNumBytesPerVertexメソッドやD3DXGetFVFVertexSize関数で求めることができます。
頂点データ内のメンバの並び順はMSDNの頂点フォーマットに載っています。

Re: 頂点変換(ワールド→スクリーン)について

Posted: 2011年10月07日(金) 08:33
by KUROKUN
ISLe 様、ありがとうございます。
今回の問題としては、メッシュの複製のFVFを自分指定のFVFフラグにすることで、解決できました。
簡易的な解決方法なので、実際のxファイルからのデータは欠落してしまう恐れがありますが、
必要な部分の情報が得られるので現状はこのままでいきたいと思います。
もし、問題があればお示しいただいたフォーマットをもとに、互換性を保つためのドライバを書きたいと思います。

閲覧していただいた方々もありがとうさいました。
ひよっこですが、またよろしくお願いします。