ホーム < ゲームつくろー! < DirectX10技術編
その4 Xファイルからモデルを読み込んでみる
DirectX技術編その2及びその3で板ポリゴンを作成してワールド空間に置き、それを表示するところまでできました。後は頂点を並べていけば複数の板ポリゴンも描画できます。しかし、人物や建物などとなるともうお手上げです。その場合は3Dモデラーなどで作成されたモデルを読み込んで表示する必要があります。
この章ではモデルファイルを読み込んで画面に表示するまでを試行錯誤してみます。なかなか込み合った章のため、サンプルをご覧になりながらの方が良いかもしれません。
@ DirectX10ではXファイルサポートが無くなった!
衝撃的な事に、DirectX10ではDirectX9時代まであったXファイル(モデルやアニメーションを格納するファイル)へのサポートがすべて無くなってしまいました。ヘルパー関数なども一切消えております。その代わりとして「DXUT Mesh FIle Format」という形式がDXUT(DirectX Utility)上でサポートされています。しかし、この形式は皆無的に広まっていないようで、エクスポータも全く見当たりません。しかも、これを使うためにDXUT自体をアプリケーションに組み込む必要があります。正直Xファイルを扱う以上に面倒です。
メッシュの描画について、2008年1月の時点で情報を与えてくれている書籍は工学社が発刊している「DirectX10 3Dプログラミング」だけのようです。Amazonさんで「DirectX10」と検索して驚愕したのですが、現時点(2008.1)で日本に唯一あるDirectX10の本はこれだけです。それどころか、英語の本もBeginning DirectX 10 Game Programming1冊きりです。今後少しずつ増えて来ると思いますが、現時点ではあまりに情報が薄すぎる状態です。
書籍DirectX10 3Dプログラミングにはメッシュを読み込むために「Wavefront OBJ」という歴史あるフォーマット形式を使っています。パーサー(構文解析器)は自前で実装しています。○×で同じことをするのは工学社さんに怒られてしまいそうなので(^-^;、この章では別の切り口で攻めてみます。
@ ID3DX10Meshインターフェイス
DirectX9にはメッシュ(ポリゴンが沢山あるモデル)を管理するID3DXMeshインターフェイスがありました。これとほぼ同様のインターフェイスとしてDirectX10にもID3DX10Meshインターフェイスが用意されています。ただし、DirectX9の時のようにXファイルからID3DX10Meshを作ってくれるヘルパー関数はありません。そこで、このインターフェイスを0から構築してみます。
まず空のID3DX10Meshインターフェイスを生成するにはD3DX10CrateMesh関数を用います:
D3DX10CreateMesh関数 HRESULT D3DX10CreateMesh(
ID3D10Device *pDevice,
CONST D3D10_INPUT_ELEMENT_DESC *pDeclaration,
UINT DeclCount,
LPCSTR pPositionSemantic,
UINT VertexCount,
UINT FaceCount,
UINT Options,
ID3DX10Mesh **ppMesh
);
pDeviceには描画デバイスを渡します。
pDeclarationにはメッシュを構成する1頂点のフォーマットであるD3D10_INPUT_ELEMENT_DESC構造体の配列を渡します。この構造体についてはDirectX10技術編その2をご参照下さい。
DeclCountはその配列の要素数です。
pPositionSemanticには頂点位置を表すセマンティクス文字列を渡します。これはDirectX10が固有の「位置セマンティクス」をもう持っていないためで、pDeclarationで渡したフォーマットのどれが頂点に該当するのかをちゃんと示す必要があるために設けられています。
VertexCountはメッシュを構成する頂点の数で0以上を指定する必要があります。
FaceCountはポリゴンの数です。こちらの上限はデフォルトで65534(216-2)ですが、次のOptionsフラグの設定で232-2にもできます。
Optionsにはメッシュ生成時のオプションです。2つ用意されていて、D3DX10_MESH_32_BITを指定するとポリゴン数を232-2枚まで扱えるようになります(実質は32bitOSではメモリ上限があるのでそこまで管理できませんが)。D3DX10_MESH_GS_ADJACENCYを設定するとそのメッシュがジオメトリシェーダ隣接情報を持っている事になります。これについては現在私も良くわかりません(^-^;;
以上が正しく設定されていればppMeshに空っぽのメッシュインターフェイスが返ります。まだイメージが沸かないと思います。次からじっくりこのインターフェイスを作っていきましょう。
A DirectX9とXファイル復活!
難しいと言いますか面倒くさいのはメッシュのデータを読み込む部分です。これは自前のフォーマットも有りなんですが、3Dモデラーがサポートしていなければモデル製作からプログラムに持って行く過程がどうしても辛くなってしまいます。モデラーがサポートしている3Dフォーマットを流用するのは有用な手です。ただその場合はプログラム上でそれを扱うためのライブラリを使うか自前でパーサーを作る必要が出てきます。いずれそれも考えようとは思いますが、この章では手っ取り早くモデルを作りたいと考えています。
そこでDirectX9を復活させてXファイルを取り扱うインターフェイスを使い、Xファイルからモデルの情報を読み取ってそれをID3DX10Meshに流し込みます。
DirectX9でXファイルからメッシュを生成するのはD3DXLoadMeshFromX関数です。この関数はDirectX9の描画デバイス(IDirect3DDevice9)を必要としますので、描画デバイスも作成します。○×ではDirectX9の描画デバイスを取得する極短プログラミングを公開しておりますので、面倒と言う方はそれをコピペしてご利用下さい。以下はIDirect3DDevice9を生成し、XファイルからID3DXMeshインターフェイスを取得した後のお話です。
B ID3DXMeshから頂点フォーマットを探る
ID3DXMeshを取得すれば、そこからメッシュの頂点位置やフォーマット、面の数など必要な情報を抽出する事が出来ます。先に紹介したD3DX10CreateMesh関数でID3DX10Meshインターフェイスを生成するには「頂点のフォーマット」「頂点数」「ポリゴン数」の情報が必要ですが、これらはすべてID3DXMeshから取得できます:
頂点数とポリゴン数の取得 ID3DXMesh *pD3DX9Mesh;
// メッシュ読み込みをここで //
// 頂点数とポリゴン数を取得
DWORD numVertex = pD3DX9Mesh->GetNumVertices(); // 頂点数
DWORD numFace; = pD3DX9Mesh->GetNumFaces(); // ポリゴン数
頂点数とポリゴン数の取得はなんら問題ありません。
頂点フォーマットは結構面倒で、ID3DXMesh::GetDeclarationメソッドで得られるD3DVERTEXELEMENT9構造体の解析が必要です。まずこの構造体の定義を見てみます:
D3DVERTEXELEMENT9構造体 typedef struct D3DVERTEXELEMENT9 {
WORD Stream;
WORD Offset;
BYTE Type;
BYTE Method;
BYTE Usage;
BYTE UsageIndex;
} D3DVERTEXELEMENT9, *LPD3DVERTEXELEMENT9;
Streamはストリーム番号です。今回はあまり関係ありません。
Offsetは頂点宣言の最初からのオフセットバイト数を表します。この情報は必須。
Typeは頂点データの型をD3DDECLTYPE列挙型で表します。float、float4などを表すフラグが結構あります。
Methodはこの頂点項目をテッセレータ(ポリゴン分割器)がどう扱うかを表すフラグです。無視しましょう(^-^;。
Usageはこの頂点項目に種類(POSITION、COLORなど)を表します。セマンティクス情報がこれに該当します。もちろん必須です。
UsageIndexはセマンティクスのインデックス番号です。COLOR0、COLOR1など同じセマンティクスを識別するわけです。必須ですね。
この構造体の情報から、DirectX10の頂点フォーマット定義であるD3D10_INPUT_ELEMENT_DESC構造体を次に挙げる関数で作り上げていきます。
C CreateD3D10InputElementDescFromD3DVERTEXELEMENT9関数・・・長い(^-^;
D3DVERTEXELEMENT9構造体配列をD3D10_INPUT_ELEMENT_DESC構造体配列に変換してみます。以下のようにUsageやTypeを見てD3D10_ELEMENT_DESC構造体のメンバを埋めていきます:
CreateD3D10InputElementDescFromD3DVERTEXELEMENT9関数 const char* g_szSemanticsPosition = "POSITION";
const char* g_szSemanticsBlendWeight = "BLENDWEIGHT";
const char* g_szSemanticsBlendIndices = "BLENDINDICES";
const char* g_szSemanticsNormal = "NORMAL";
const char* g_szSemanticsPSize = "PSIZE";
const char* g_szSemanticsTexCoord = "TEXCOORD";
const char* g_szSemanticsTangent = "TANGENT";
const char* g_szSemanticsBinormal = "BINORMAL";
const char* g_szSemanticsTessFactor = "TESSFACTOR";
const char* g_szSemanticsPositionT = "POSITIONT";
const char* g_szSemanticsColor = "COLOR";
const char* g_szSemanticsFog = "FOG";
const char* g_szSemanticsDepth = "DEPTH";
const char* g_szSemanticsSample = "SAMPLE";
int CreateD3D10InputElementDescFromD3DVERTEXELEMENT9(
D3DVERTEXELEMENT9 pElement[MAX_FVF_DECL_SIZE],
D3D10_INPUT_ELEMENT_DESC pOut[MAX_FVF_DECL_SIZE] )
{
int numDecl = 0;
for( unsigned int i = 0; i < MAX_FVF_DECL_SIZE; i++ ) {
// Usageを見てセマンティクスを決定
const char *Semantics;
switch( pElement[i].Usage ) {
case D3DDECLUSAGE_POSITION: Semantics = g_szSemanticsPosition; break;
case D3DDECLUSAGE_BLENDWEIGHT: Semantics = g_szSemanticsBlendWeight; break;
case D3DDECLUSAGE_BLENDINDICES: Semantics = g_szSemanticsBlendIndices; break;
case D3DDECLUSAGE_NORMAL: Semantics = g_szSemanticsNormal; break;
case D3DDECLUSAGE_PSIZE: Semantics = g_szSemanticsPSize; break;
case D3DDECLUSAGE_TEXCOORD: Semantics = g_szSemanticsTexCoord; break;
case D3DDECLUSAGE_TANGENT: Semantics = g_szSemanticsTangent; break;
case D3DDECLUSAGE_BINORMAL: Semantics = g_szSemanticsBinormal; break;
case D3DDECLUSAGE_TESSFACTOR: Semantics = g_szSemanticsTessFactor; break;
case D3DDECLUSAGE_POSITIONT: Semantics = g_szSemanticsPositionT; break;
case D3DDECLUSAGE_COLOR: Semantics = g_szSemanticsColor; break;
case D3DDECLUSAGE_FOG: Semantics = g_szSemanticsFog; break;
case D3DDECLUSAGE_DEPTH: Semantics = g_szSemanticsDepth; break;
case D3DDECLUSAGE_SAMPLE: Semantics = g_szSemanticsSample; break;
default:
return -1;
}
// Typeを見てフォーマットを判断
DXGI_FORMAT D3D10Format;
switch( pElement[i].Type ) {
case D3DDECLTYPE_FLOAT1: D3D10Format = DXGI_FORMAT_R32_FLOAT; break;
case D3DDECLTYPE_FLOAT2: D3D10Format = DXGI_FORMAT_R32G32_FLOAT; break;
case D3DDECLTYPE_FLOAT3: D3D10Format = DXGI_FORMAT_R32G32B32_FLOAT; break;
case D3DDECLTYPE_FLOAT4: D3D10Format = DXGI_FORMAT_R32G32B32A32_FLOAT; break;
case D3DDECLTYPE_D3DCOLOR: D3D10Format = DXGI_FORMAT_R32G32B32A32_FLOAT; break;
case D3DDECLTYPE_UBYTE4: D3D10Format = DXGI_FORMAT_R8G8B8A8_UINT; break;
case D3DDECLTYPE_SHORT2: D3D10Format = DXGI_FORMAT_R16G16_SINT; break;
case D3DDECLTYPE_SHORT4: D3D10Format = DXGI_FORMAT_R16G16B16A16_SINT; break;
case D3DDECLTYPE_UBYTE4N: D3D10Format = DXGI_FORMAT_R8G8B8A8_UNORM; break;
case D3DDECLTYPE_SHORT2N: D3D10Format = DXGI_FORMAT_R16G16_SNORM; break;
case D3DDECLTYPE_SHORT4N: D3D10Format = DXGI_FORMAT_R16G16B16A16_SNORM; break;
case D3DDECLTYPE_USHORT2N: D3D10Format = DXGI_FORMAT_R16G16_UNORM; break;
case D3DDECLTYPE_USHORT4N: D3D10Format = DXGI_FORMAT_R16G16B16A16_UNORM; break;
case D3DDECLTYPE_UDEC3: D3D10Format = DXGI_FORMAT_R10G10B10A2_UINT; break;
case D3DDECLTYPE_DEC3N: D3D10Format = DXGI_FORMAT_R10G10B10A2_UNORM; break;
case D3DDECLTYPE_FLOAT16_2: D3D10Format = DXGI_FORMAT_R16G16_FLOAT; break;
case D3DDECLTYPE_FLOAT16_4: D3D10Format = DXGI_FORMAT_R16G16B16A16_FLOAT; break;
case D3DDECLTYPE_UNUSED:
numDecl = i + 1;
return numDecl;
default:
return -1;
}
// 構造体作成
pOut[i].SemanticName = Semantics;
pOut[i].SemanticIndex = pElement[i].UsageIndex;
pOut[i].Format = D3D10Format;
pOut[i].InputSlot = 0;
pOut[i].AlignedByteOffset = pElement[i].Offset;
pOut[i].InputSlotClass = D3D10_INPUT_PER_VERTEX_DATA;
pOut[i].InstanceDataStepRate = 0;
numDecl++;
}
return numDecl++;
}
長い関数ですが、DirectX9と10の頂点情報を対応させているだけなので難しい部分はありません。兎にも角にもこの関数を使えばID3DXMesh::GetDeclarationメソッドで取得できるD3DVERTEXELEMENT9からD3D10_INPUT_ELEMENT_DESC構造体が生成できるわけです。
C ID3DXMeshの情報をID3DX10Meshへ
AおよびBの情報から空っぽのID3DX10Meshインターフェイスを作る事ができます:
ID3DX10Meshインターフェイスを生成 D3DXVERTEXELEMENT9 DX9VertexDecl[MAX_FVF_DECL_SIZE];
D3D10_INPUT_ELEMENT_DESC Declaration[MAX_FVF_DECL_SIZE];
// 頂点フォーマットを変換
DWORD DeclCount = CreateD3D10InputElementDescFromD3DVERTEXELEMENT9(
DX9VertexDecl, Declaration );
// 空メッシュ生成
ID3DX10Mesh *pD3DX10Mesh;
D3DX10CreateMesh( pDev, Declaration, DeclCount,
"POSITION", numVertex, numFace, 0, pD3DX10Mesh );
続いてこの空っぽのインターフェイスにメッシュの情報を流し込みます。必須の情報と対応は次の通りです:
必須情報 ID3DX10Mesh ID3DMesh 頂点 GetVertexBuffer GetVertexBuffer インデックス GetIndexBuffer GetIndexBuffer 属性テーブル GetAttributeTable GetAttributeTable
1つずつ見ていきます。
○ 頂点情報
ID3DX10Meshにメッシュの頂点情報を渡すにはID3DXMesh::GetVertexBufferメソッドで取得した頂点バッファからID3DX10Mesh::GetVertexBufferで得たポインタの先に情報を書き込みます。ID3DX10Mesh::GetVertexBufferは次のように定義されています:
ID3DX10Mesh::GetVertexBufferメソッド HRESULT GetVertexBuffer(
UINT iBuffer,
ID3DX10MeshBuffer **ppVertexBuffer
);
iBufferには頂点バッファのインデックスを与えます。ID3DX10Meshは複数の頂点バッファで構成できるようです。しかし、大抵は1つの頂点バッファで事足りますので、ここには0が来る事が殆どです。
ppVertexBufferには指定したインデックスの頂点バッファインターフェイスが返ります。
ID3DX10MeshBufferインターフェイスは1つのメッシュバッファを管理します。バッファにアクセスするにはID3DX10MeshBuffer::Mapメソッドを用います:
ID3DX10MeshBuffer::Mapメソッド HRESULT Map(
void **ppData,
SIZE_T *pSize
);
ppDataにはバッファへのポインタが、pSizeにはバッファサイズがそれぞれ返ります。Mapメソッドを呼んだ後は必ずUnmapメソッドを呼ぶ必要があります。
ID3DXMeshからの頂点情報を格納するソースは次のようになります:
頂点情報を格納 // ID3DXMesh(DirectX9)のバッファをID3DX10Meshに書き込む
ID3DX10MeshBuffer *pD3DX10MeshBuffer;
void *pD3DX9Buf, pD3DX10Buf;
SIZE_T sz;
pD3DX9Mesh->LockVertexBuffer(D3DLOCK_READONLY, &pD3DX9Buf);
{
pD3DX10Mesh->GetVertexBuffer( 0, &pD3DX10MeshBuffer );
pD3DX10MeshBuffer->Map( &pD3DX10Buf, &sz );
memcpy( pD3DX10Buf, pD3DX9Buf, sz );
pD3DX10MeshBuffer->Unmap();
}
pD3DX9Mesh->UnlockVertexBuffer();
○ インデックス情報
頂点の並びを記述するインデックス情報も頂点情報とほぼ同じような方法で抽出できます。ID3DXMesh::GetIndexBufferメソッドでIDirect3DIndexBuffer9インターフェイスを取得し、Lockすることでバッファへのポインタを取得できます。一方ID3DX10Mesh::GetIndexBufferメソッドでDirectX10側の空のインデックスバッファへアクセスできるので、そこにコピーして完了です。似たようなソースになるので一気に示します:
インデックス情報を格納 ID3DX10MeshBuffer *pD3DX10IndexBuffer;
void *pD3DX9IDBuf, *pD3DX10IDBuf;
SIZE_T idsz;
pD3DX9Mesh->LockIndexBuffer(D3DLOCK_READONLY, &pD3DX9IDBuf);
{
pD3DX10Mesh->GetIndexBuffer( &pD3DX10IndexBuffer );
pD3DX10IndexBuffer->Map( &pD3DX10IDBuf, &idsz );
memcpy( pD3DX10IDBuf, pD3DX9IDBuf, idsz );
pD3DX10IndexBuffer->Unmap();
}
pD3DX9Mesh->UnlockIndexBuffer();
○ 属性テーブル情報
属性テーブルとは1つのメッシュをグループに分けた「サブセット」の情報を格納したリストの事です。この情報はDirectX9と10でまったく同じですが、構造体の型が違います。
DirectX9ではID3DXMesh::GetAttributeTableメソッドで取得するD3DXATTRIBUTERANGE構造体リストがそれに当たりますが、DirectX10ではID3DX10Mesh::GetAttributeTableメソッドで取得できるD3DX10_ATTRIBUTE_RANGE構造体配列がそれを担います。この情報を抽出します:
属性テーブル情報取得 // 属性テーブルのコピー
pD3DX9Mesh->GetAttributeTable( NULL, &sz );
D3DXATTRIBUTERANGE *pD3D9AttTable = new D3DXATTRIBUTERANGE[sz];
pD3DX9Mesh->GetAttributeTable( pD3D9AttTable, &sz );
D3DX10_ATTRIBUTE_RANGE *pD3D10AttTable = new D3DX10_ATTRIBUTE_RANGE[sz];
for ( unsigned int i = 0; i < sz; i++ )
{
pD3D10AttTable[i].AttribId = pD3D9AttTable[i].AttribId;
pD3D10AttTable[i].FaceStart = pD3D9AttTable[i].FaceStart;
pD3D10AttTable[i].FaceCount = pD3D9AttTable[i].FaceCount;
pD3D10AttTable[i].VertexStart = pD3D9AttTable[i].VertexStart;
pD3D10AttTable[i].VertexCount = pD3D9AttTable[i].VertexCount;
}
pD3DX10Mesh->SetAttributeTable( pD3D10AttTable, sz );
delete[] pD3D9AttTable;
delete[] pD3D10AttTable;
行っている事はID3DXMeshからD3DXATTRIBUTERANGE構造体の配列、ID3DX10MeshからD3DX10_ATTRIBUTE_RANGE構造体の配列を取得しその情報を渡しています。それをID3DX10Mesh::SetAttributeTableメソッドで設定しています。設定後情報はいりませんので削除してかまいません。
さて、以上でメッシュに必要な情報を渡す事ができました。ID3DXMeshはメモリの圧迫を避けるために削除してしまいましょう:
属性テーブル情報取得 pD3DX9Mesh->Release();
D ハメを潜り抜けて描画だ!
当初上の設定でもう描画できるのかなと思ったのですが、やはりありました「ハメ」。それも2つ。まず1つは頂点レイアウトを設定する必要があります。これはDirectX10ではプログラマが自由にシェーダの入力セマンティクスを決めて良いからでして、入植情報との整合性をあわせるのが必須であるためです。ただありがたいことに、頂点レイアウトの作成に必要な「頂点フォーマット」の情報はすでにあのなが〜い関数名である「CreateD3D10InputElementDescFromD3DVERTEXELEMENT9関数」で作成しておりました。これを素直に使って頂点レイアウトを作成します:
頂点レイアウト作成 // シェーダからエフェクト作成
D3D10_PASS_DESC PassDesc;
ID3D10InputLayout *pVertexLayout;
ID3D10EffectTechnique *pTechnique;
ID3D10Effect *pEffect;
ID3D10Blob *pBlob;
D3DX10CreateEffectFromFile( L"MyShader.fx", 0, 0,
"fx_4_0", D3D10_SHADER_ENABLE_STRICTNESS, 0, pDev, 0, 0, &pEffect, &pBlob, 0 );
// 頂点レイアウト作成
pTechnique = pEffect->GetTechniqueByName( "SimpleRender" );
pTechnique->GetPassByIndex( 0 )->GetDesc( &PassDesc );
pDev->CreateInputLayout( Declaration, DeclCount, PassDesc.pIAInputSignature,
PassDesc.IAInputSignatureSize, &pVertexLayout );
// デバイスに頂点レイアウト登録
pDev->IASetInputLayout( pVertexLayout );
もう1つのハメ。それは、メッシュを作成した後に「ID3DX10Mesh::CommitToDeviceメソッド」を必ず呼ぶ必要がある事です。このメソッドはメッシュの情報を描画デバイスに関連付けます。これが無いと描画は失敗してしまいます。メッシュの情報を更新した後も必ず描画前にこのメソッドを呼ばなければなりません:
属性テーブル情報取得 pD3DX10Mesh->CommitToDevice();
これで描画準備は整いました。後はシェーダのパスを取得して描画するだけです:
メッシュを描画 pDev->ClearRenderTargetView( pRenderTargetView, ClearColor );
pTechnique->GetDesc( &techDesc );
for ( UINT p = 0; p < techDesc.Passes; ++p ) {
pTechnique->GetPassByIndex( p )->Apply( 0 );
pD3DX10Mesh->DrawSubset(0);
}
pSwapChain->Present( 0, 0 );
サブセットが複数ある場合は赤太字部分をループにしてDrawSubsetの引数をイテレートさせます。
E 深度ステンシルバッファの作成を忘れてました
上の状態でモデルを描画すると、ポリゴンの前後関係がおかしくなってしまいます。これは描画ターゲットに深度ステンシルバッファが設定されていないからです。DirectX9の時にはデバイス作成時に深度ステンシルバッファをフラグで定義していました。しかしDirectX10では深度ステンシルバッファもしかるべき方法で作成してターゲットにアタッチする必要があります。これが結構面倒なんです(^-^;
まず、深度ステンシルバッファとなるテクスチャを作成します。これにはID3DX10Device::CreateTexture2Dメソッドを用います:
ID3DX10Device::CreateTexture2Dメソッド HRESULT CreateTexture2D(
const D3D10_TEXTURE2D_DESC *pDesc,
const D3D10_SUBRESOURCE_DATA *pInitialData,
ID3D10Texture2D **ppTexture2D
);
pDescには作成するテクスチャの情報であるD3D10_TEXTURE2D_DESC構造体を渡します。
pInitialDataにはテクスチャに流し込むデータを与えます。初期化しないで良い場合はNULL出構いません。
ppTexture2Dには作成したテクスチャが返ります。
D3D10_TEXTURE2D_DESC構造体は次の通りです:
D3D10_TEXTURE2D_DESC構造体 typedef struct D3D10_TEXTURE2D_DESC {
UINT Width;
UINT Height;
UINT MipLevels;
UINT ArraySize;
DXGI_FORMAT Format;
DXGI_SAMPLE_DESC SampleDesc;
D3D10_USAGE Usage;
UINT BindFlags;
UINT CPUAccessFlags;
UINT MiscFlags;
} D3D10_TEXTURE2D_DESC;
WidthとHeightはテクスチャの幅と高さです。
MipLevelsはミップマップレベルを設定します。1を設定するとミップマップは作成されません。0を設定すると1×1のサイズまでミップマップテクスチャが作成されます。
ArraySizeにはテクスチャ配列のサイズを指定します。DirectX10ではテクスチャは配列で扱われます。1枚で良い場合はもちろん「1」を指定します。
Formatは作成するテクスチャのフォーマットです。深度ステンシルテクスチャを作成するには専用のフラグ(DXGI_FORMAT_D24_UNORM_S8_UINT、DXGI_FORMAT_D32_FLOATなど)を指定します。
SampleDescはマルチサンプリングの精度をDXGI_SAMPLE_DESC構造体で指定します。
Usageにはテクスチャの使われ方をフラグ指定します。大抵はD3D10_USAGE-DEFAULTでOKです。
BindFlagsにはこのテクスチャの使用目的をフラグで指定します。深度ステンシルバッファの場合はD3D10_BIND_DEPTH_STENCILです。
CPUAccessFlagsにはCPUのアクセス権限を指定します。深度ステンシルバッファをCPUで操作しない場合は0を指定します。
MiscFlagsには瑣末的な付加フラグを指定します。変な事をしない限りは0で十分です。
深度ステンシルバッファを作成する場合の典型的な設定例は以下の通りです:
深度ステンシルバッファの定義例 D3D10_TEXTURE2D_DESC DepthDesc;
DepthDesc.Width = 800;
DepthDesc.Height = 600;
DepthDesc.MipLevels = 1;
DepthDesc.ArraySize = 1;
DepthDesc.Format = DXGI_FORMAT_D24_UNORM_S8_UINT;
DepthDesc.SampleDesc.Count = 1; // マルチサンプルをしない
DepthDesc.SampleDesc.Quality = 0; // 品質は一番下
DepthDesc.Usage = D3D10_USAGE_DEFAULT;
DepthDesc.BindFlags = D3D10_BIND_DEPTH_STENCIL;
DepthDesc.CPUAccessFlags = 0;
DepthDesc.MiscFlags = 0;
// 深度ステンシルテクスチャ作成
ID3D10Texture2D *pDepthTexture;
pDev->CreateTexture2D( &DepthDesc, NULL, &pDepthTexture);
続いて深度ステンシルテクスチャを内部に保持する深度ステンシルビュー(ID3D10DepthStencilView)を作成します。これはID3D10Device::CreateDepthStencilViewメソッドを用います。これはお決まりのコードになります:
深度ステンシルビューを作成 D3D10_DEPTH_STENCIL_VIEW_DESC DepthViewDesc;
DepthViewDesc.Format = DepthDesc.Format; // 深度ステンシル定義と同じにする
DepthViewDesc.ViewDimension = D3D10_DSV_DIMENSION_TEXTURE2D;
DepthViewDesc.Texture2D.MipSlice = 0;
ID3D10DepthStencilView *pDepthStencilView;
pDev->CreateDepthStencilView( pDepthTexture, &DepthViewDesc, pDepthStencilView );
これで深度ステンシルテクスチャを持った深度ステンシルビューができますので、描画デバイスにレンダーターゲットを設定する(ID3DX10Device::OMSetRenderTargets使用)時にこのビューを指定します:
深度ステンシルビューを持ったレンダーターゲットをデバイスに設定 pDev->OMSetRenderTargets( 1, &pRenderTargetView, &pDepthStencilView );
以上で深度ステンシルバッファの設定は終了です。何とも面倒なもんです(^-^;;。
描画時には深度ステンシルテクスチャをクリアする必要があります。これにはID3DX10Device::ClearDepthStencilViewメソッドを用います:
深度ステンシルテクスチャをクリア pDev->ClearDepthStencilView(
pDepthStencilView, // 深度ステンシルビュー
D3D10_CLEAR_DEPTH | D3D10_CLEAR_STENCIL, // クリアするバッファ
1.0f, // 深度のクリア値
0.0f // ステンシルのクリア値
);
ちょっと駆け足でしたが、ID3DX10Meshインターフェイスを使ってXファイルで定義されたモデルを描画する手順の一例を示してみました。当初DirectX10にモデル読み込みのサポートが無い事を知って「これ、普通の人は手が出ないジャン」と思ったのですが、DirectX9の力を借りて何とか読み込みができる事がわかりました。この章のサンプルプログラムでXファイルからID3DX10Meshインターフェイスを作成するD3DX10CreateMeshFromX関数を公開したいと思います。固定メッシュしかできませんが役に立つと思いますので是非ご活用下さい。