アットウィキロゴ
ノート > Direct3D 入門 > Xファイルの3Dモデルの表示

Xファイルの3Dモデル(メッシュ)を描画する方法を説明します。

サンプルプログラム

Subversionリポジトリ内、下記の場所にプロジェクトフォルダーがあります
trunk/directx/ShowXFile_main20100511

やること

  • Xファイルの3Dモデル(メッシュ)を読み込んで3D空間内に描画する

用語

プリミティブ
点と面が非常に少ないジオメトリのこと。スプライトもこの中に含まれる
メッシュ
頂点と面が多く比較的複雑なジオメトリのこと。通常はプログラム内で動的に生成したりはせず、「Xファイル」という別ファイルからデータを読み込んで作る

注意:
  • 一般的に「プリミティブ」とは3角形か4角形のポリゴン、および単純な立方体、球、円錐、円柱まで。
    • トーラス(ドーナツ形状)までに至ると頂点とポリゴンが非常に多くなるので「プリミティブ」とは普通は呼ばない。
  • 「Xファイル=メッシュ」ではない。
    • Xファイルはメッシュを作成する手段の一つ
      • 非常に便利なため事実上メッシュの作成はXファイルを使っている
  • Xファイルの作成は3D-CGソフトを使う
    • フリーソフトだと「六角大王」や「メタセコイア」など

解説

スプライトのサンプルと異なるのは、2つ
  • InitD3d 関数内にXファイル読み込みルーチンを追加
  • 「Render関数」

InitD3d 関数

// Xファイルからメッシュをロードする	
LPD3DXBUFFER pD3DXMtrlBuffer = NULL;

if( FAILED( D3DXLoadMeshFromX( "Drug.x", D3DXMESH_SYSTEMMEM, g_pd3dDevice, NULL,
  &pD3DXMtrlBuffer, NULL, &dwNumMaterials, &pMesh ) ) )
{
  MessageBox(NULL, "Xファイルの読み込みに失敗しました",NULL, MB_OK);
  return E_FAIL;   
}
  • 「D3DXLoadMeshFromX 関数」により、メッシュを読み込む
    • この関数をコールすれば大抵のことはやってくれる
    • でも、プログラムに書いてあるようにアプリ側で書かなければならない処理も結構ある
  • アプリ側で書かなければならない主な処理は「マテリアルの処理」・「テクスチャの処理」
    • とりあえずは、このように書くものだと思っておいて問題ない
    • 自信でロード関数を作る機会があるとすれば、この部分を編集すれば事足りる

Render 関数

この関数には、3Dにおける基礎が詰まっており、
特に3つのトランスフォーム(座標系変換)をこの関数内で行っている

//ワールドトランスフォーム(絶対座標変換)
D3DXMATRIXA16 matWorld,matRotation;
D3DXMatrixRotationY( &matWorld, timeGetTime()/3000.0f );
D3DXMatrixRotationX( &matRotation, 0.5f );
D3DXMatrixMultiply(&matWorld,&matWorld,&matRotation);
g_pd3dDevice->SetTransform( D3DTS_WORLD, &matWorld );
  • まず、ジオメトリのローカル座標からワールド座標(絶対座標)へ変換

// ビュートランスフォーム(視点座標変換)
D3DXVECTOR3 vecEyePt( 0.0f, 0.1f,-0.1f ); //カメラ(視点)位置
D3DXVECTOR3 vecLookatPt( 0.0f, 0.0f, 0.0f );//注視位置
D3DXVECTOR3 vecUpVec( 0.0f, 1.0f, 0.0f );//上方位置
D3DXMATRIXA16 matView;
D3DXMatrixLookAtLH( &matView, &vecEyePt, &vecLookatPt, &vecUpVec );
g_pd3dDevice->SetTransform( D3DTS_VIEW, &matView );
  • 次に、ワールド座標をさらにカメラ座標に変換

// プロジェクショントランスフォーム(射影変換)
D3DXMATRIXA16 matProj;
D3DXMatrixPerspectiveFovLH( &matProj, D3DX_PI/4, (FLOAT)WINDOW_WIDTH/(FLOAT)WINDOW_HEIGHT, 0.1f, 10.0f );
g_pd3dDevice->SetTransform( D3DTS_PROJECTION, &matProj );     
  • 最後にカメラ座標からスクリーン座標に変換

参考:

// ライトをあてる 白色で鏡面反射ありに設定
D3DXVECTOR3 vecDirection(0,-1,1);
D3DLIGHT9 light;
ZeroMemory( &light, sizeof(D3DLIGHT9) );
light.Type       = D3DLIGHT_DIRECTIONAL;
light.Diffuse.r  = 1.0f;
light.Diffuse.g  = 1.0f;
light.Diffuse.b  = 1.0f;    
light.Specular.r=1.0f;
light.Specular.g=1.0f;
light.Specular.b=1.0f;
D3DXVec3Normalize( (D3DXVECTOR3*)&light.Direction, &vecDirection );
light.Range       = 20.0f;
g_pd3dDevice->SetLight( 0, &light );
g_pd3dDevice->LightEnable( 0, TRUE );
  • 「ライト」を作っている部分

// レンダリング
g_pd3dDevice->Clear( 0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER ,
  D3DCOLOR_XRGB(100,100,100), 1.0f, 0 );

if( SUCCEEDED( g_pd3dDevice->BeginScene() ) )
{
  for( DWORD i=0; i<dwNumMaterials; i++ )
{
  g_pd3dDevice->SetMaterial( &pMeshMaterials[i] );
  g_pd3dDevice->SetTexture( 0, pMeshTextures[i] ); 
  pMesh->DrawSubset( i );
}
  g_pd3dDevice->EndScene();
}	
g_pd3dDevice->Present( NULL, NULL, NULL, NULL );	
  • スプライトの場合ではレンダリング(描画処理)は1行で済んだ
    • メッシュの場合は「マテリアル」と「テクスチャ」をセットするコードが必要になる
  • メッシュは1個以上のマテリアルから成り立っている
    • マテリアルごとにマテリアルとテクスチャが異なるのが普通
      • なので、マテリアルの数だけループさせ、ループ1回転ごとにマテリアルとテクスチャを当該マテリアルのものでセットしなくてはならない

メモ

'LPSTR' から 'LPCWSTR' に変換できません

こういったエラーが出た場合、
「プロジェクトのプロパティ」
を開き、
「構成プロパティ」-「全般」
内の
「文字セット」
「マルチバイト文字セットを使用する」
にして再びビルドし直してみること。
最終更新:2010年05月13日 10:03