「 DirectX 」一覧

DirectX10 (D3D10)


Tutorial04

今回はTutorial04を見てました。このサンプルは立方体(キューブ)の表示サンプルでした。
前回のTutorial02の部分に比べて増えたところと言えば、IndexBufferを作っているところです。
ほかには、シェーダーで透視変換させているところ。大きく変わった部分はないです。

IndexBufferもVertexBufferと同じくBufferDescriptionとSubresourceDescriptionの2つを設定して、デバイスのメソッドで作成するだけです。

変更になってる部分はデータをどう使うか指示するところ、
「D3D10_BIND_INDEX_BUFFER」「D3D10_BIND_VERTEX_BUFFER」の違いくらいです

描画する前には以下のように

各バッファをデバイスに設定し、プリミティブタイプを指定しておきます。この辺が若干以前と変わっています。
前はDrawPrimitive系でプリミティブタイプを指定していました。

また、インデックス用のフォーマット指定(D3DFMT_INDEX16/32)がありましたが、
どうやら今回からは統一されたようです。

気になったのはこのサンプル、インデックスに32ビット指定してます。
従来は16ビット(WORD型)を基本的に使っていたのに、このように変更となるとデバイスの最適化もこれからは32ビットインデックスが主流となっていくんですかねぇ…。(※ 現状のデバイス(当時)は16ビットインデックスに最適化されています)

シェーダーに対して、各マトリックスをセットして、パスを回しています。
見てわかるように描画のDrawIndexedの引数が減っています。すっきりしましたね〜。あとDX9世代でBegin/BeginPassで行っていたパスを回す処理が変更になっています。テクニックのDescから値を取得して、回数と使用指示を設定しているようです。

この辺は使う側としてはあまり変更がないといってもいいでしょう。
あまり大きな変更ではないように思います。

気になったのは今までシェーダー定数を書き換えたときには、CommitChangesなどのメソッドを呼んで確定させていた気がしますが、それが消えています。

シェーダーコード

HLSLの中身も若干変わっています。

今までfloat4x4とかやっていた部分がmatrixになっています!

Passの部分も

となっており、ぱっとみてわかる程度の変更です。

ここにジオメトリシェーダーの記述があって、そのうちいじってみたいなとわくわくです。


DirectX10 (D3D10)


Tutorial02解説

今回は、DX10での三角形(ポリゴン)描画までのサンプルを読み解いてみました。
前回までの初期化にプラスして、頂点データの作成、シェーダーの作成と要素が詰まってます。

シェーダーの作成は以下のようになってました。若干自分なりのコメントをつけてみました。

間違っている場合もあると思いますが、気づいた人はコメントでご指摘ください。(^^;

[code lang=”cpp”]
// Create the effect
// このフラグは新しいHLSLのセマンティクスを有効にするため?
// 従来のHLSLコードを通過させないためのフラグである模様
DWORD dwShaderFlags = D3D10_SHADER_ENABLE_STRICTNESS;
#if defined( DEBUG ) || defined( _DEBUG )
dwShaderFlags |= D3D10_SHADER_DEBUG;
#endif
hr = D3DX10CreateEffectFromFile( L"Tutorial02.fx", NULL, NULL, "fx_4_0", dwShaderFlags, 0,
g_pd3dDevice, NULL, NULL, &g_pEffect, NULL, NULL );
[/code]

どうやらDX9時代と変更がない感じです。プロファイルが fx_4_0になったくらいでしょうか。
シェーダーのフラグでDX10用のHLSLを要求しているように見えます。

fxファイルを覗くとわかるのですが、technique10や今までに見慣れないセマンティクスが使われています。
これらを要求するためのフラグみたいです。

[code lang=”cpp”]
// Define the input layout
D3D10_INPUT_ELEMENT_DESC layout[] =
{
{ "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D10_INPUT_PER_VERTEX_DATA, 0 },
};
UINT numElements = sizeof(layout)/sizeof(layout[0]);
// Create the input layout
D3D10_PASS_DESC PassDesc;
g_pTechnique->GetPassByIndex( 0 )->GetDesc( &PassDesc );
// 上記で設定したinput elementと、シェーダーのバイトコードからinputレイアウトの作成.
hr = g_pd3dDevice->CreateInputLayout( layout, numElements, PassDesc.pIAInputSignature, PassDesc.IAInputSignatureSize, &g_pVertexLayout );
if( FAILED( hr ) ) return hr;
// Set the input layout
g_pd3dDevice->IASetInputLayout( g_pVertexLayout );
// Create vertex buffer
SimpleVertex vertices[] =
{
D3DXVECTOR3( 0.0f, 0.8f, 0.5f ),
D3DXVECTOR3( 0.5f, -0.5f, 0.5f ),
D3DXVECTOR3( -0.5f, -0.5f, 0.5f ),
};
D3D10_BUFFER_DESC bd;
bd.Usage = D3D10_USAGE_DEFAULT;
bd.ByteWidth = sizeof( SimpleVertex ) * 3;
bd.BindFlags = D3D10_BIND_VERTEX_BUFFER;
bd.CPUAccessFlags = 0;
bd.MiscFlags = 0;
D3D10_SUBRESOURCE_DATA InitData;
InitData.pSysMem = vertices;
hr = g_pd3dDevice->CreateBuffer( &bd, &InitData, &g_pVertexBuffer );
if( FAILED( hr ) ) return hr;
// Set vertex buffer
UINT stride = sizeof( SimpleVertex );
UINT offset = 0;
g_pd3dDevice->IASetVertexBuffers( 0, 1, &g_pVertexBuffer, &stride, &offset );
// Set primitive topology
g_pd3dDevice->IASetPrimitiveTopology( D3D10_PRIMITIVE_TOPOLOGY_TRIANGLELIST );
[/code]

シェーダーのバイトコードと、頂点宣言二つからinputlayoutを作成します。
そしてこのレイアウトはIA(InputAssembler)にセット。
この次に、ようやく頂点バッファの作成に取りかかります。

DX9時代とかなり変更になっているのがこの部分。
BufferDescriptionとSubresourceDescriptionの二つから頂点バッファを作成しています。

各DESCに値をセットして、初期化時にデータを転送してしまうっぽいです。

このD3D10_USAGE_DEFAULTフラグでは、GPUからのアクセスはできて、CPUからのアクセスは禁止という意味だそうです。
DX10からはすべてのバッファがMap(従来のLock)ができるわけではなくなりました。

つまり上記の例ではMapすら禁止ということで、初期化時にシステムメモリ上のデータを設定することで、初回1度だけデータ転送しているということなんでしょう。

SubResourceとはなんぞや?

よくわからなかったので調べてみました。

上記でもSubResourceDescription を設定してます。
このdescではバッファの初期データを与えるためにセットしています。

SubresourceDescriptionは実際のリソースデータを示し、データのサイズとレイアウトに関する情報を保持します。

どこかにも記述があったのですが、Subresourceっていわゆる実データなんでしょうか。
Surfaceに相当とか書いてあった気が…。


DirectX10 (D3D10)


基本からさわってみよう。D3D10

まずは2007Apr版のDXSDKをダウンロードして、サンプルを実行してみてました。
DirectX10がどう動いてくれるか、わくわくしながらサンプルを実行したのですが、

・・・意外と安定動作しない(><

ものによっては放置しておくだけで、GPUが停止し復帰しましたとのメッセージが出まくるし、プログラムの終了時にはエラーメッセージのダイアログが出るし。まだまだきちんと整備されていないということでしょうか。

あとこれは8600GTという点だからかもしれませんが、
ジオメトリシェーダー使ったサンプルが遅い!

極端に速度低下してました。CubeMapGSのサンプルなんか車モデルにしたら 5fpsくらいになってましたし。

それでも基本的にDX9と切り替えられるタイプのサンプルでは、
DX10のほうが高速動作している感じでした。FPSの向上もみられました。

Tutorial01解説もどき

自分のメモも含めて少し解説。DX9の頃と違って、かなり初期化周りも変更になっている。
Tutorial01.cppのソースを抜き出して以下に書いてみます。

※値のセット部分は省いてあるのでコピペしても動きません。

[code lang=”cpp”]
DXGI_SWAP_CHAIN_DESC sd; ZeroMemory( &sd, sizeof(sd) ); // この後、値を詰めていく
for( UINT driverTypeIndex = 0; driverTypeIndex < numDriverTypes; driverTypeIndex++ ) {
g_driverType = driverTypes[driverTypeIndex]; // デバイスとスワップチェインを作成.
hr = D3D10CreateDeviceAndSwapChain( NULL, g_driverType, NULL, createDeviceFlags, D3D10_SDK_VERSION, &sd, &g_pSwapChain, &g_pd3dDevice );
if( SUCCEEDED( hr ) ) break;
}

// 次にRenderTargetViewを作成。リソースの種類は2DTextureってことですね
ID3D10Texture2D *pBackBuffer;
hr = g_pSwapChain->GetBuffer( 0, __uuidof( ID3D10Texture2D ), (LPVOID*)&pBackBuffer );
if( FAILED(hr) ) return hr;
hr = g_pd3dDevice->CreateRenderTargetView( pBackBuffer, NULL, &g_pRenderTargetView );
pBackBuffer->Release();
if( FAILED(hr) ) return hr;

// 作成したビューをレンダーターゲットとしてセットする
g_pd3dDevice->OMSetRenderTargets( 1, &g_pRenderTargetView, NULL );[/code]

どうやら、スワップチェインを作成を作成した後、そのスワップチェインの中にあるバッファデータを取得。

そのデータを2Dのテクスチャデータとしてバインドし直してレンダーターゲット設定している、といったところでしょうか。

Render()処理

[code lang=”cpp”]
float ClearColor[4] = { 0.0f, 0.125f, 0.3f, 1.0f }; //red,green,blue,alpha
g_pd3dDevice->ClearRenderTargetView( g_pRenderTargetView, ClearColor );
g_pSwapChain->Present( 0, 0 );
[/code]

これをみても明らかなように、レンダーターゲットに対してクリアしてます。
今まではPresentもpd3ddeviceでしたが、今回からはPresentはスワップチェインに対して操作しています。
より明確に分かれた感じがしますね。とりあえずTutorial01では、このくらいしか従来と大きく変わった部分は無さそうです。

あ、フォーマットの指定はかなり変わってました。よりデータ型を意識するものになっていました。


メモリリーク追跡


DirectXのメモリリーク

DirectXを使ったプログラムを作っていて、VRAMに確保したデータを解放せずに終了させてしまうこと、よくあることです。
特に適当に作ったプログラムや、実験を重ねて複雑になってしまったものによくあります。

そういうとき、DirectXのデバッグランタイムを使用しているのであれば、
以下のようなメッセージがプログラム終了時に表示されます

こういうときに、実はデバッグランタイムを使用していればどこのタイミングで確保したモノなのかがチェックできるらしいです。

上記の、AllocIDの部分でデバッガのブレークを貼ることが出来ますが、
手元でチェックしてみたところ、一つの目安にしかならない感じがします。

定番なのかもしれませんが、このIDの数が大きい方から処理していった方が良さそうです。
たとえば、上記AllocID=1ですと、CreateDeviceでデバイス作成したところで引っかかります。

考察としては、COMが参照カウンタ方式なため、参照された瞬間にカウンタがインクリメントされ、のちのちまで残ってしまったために、上記で表示されているのだと考えられます。

よって、数が大きいモノ、最後に参照カウンタをいじって生き残っているモノからデバッグ(解放処理?)を入れていくのは正しいのではないかと思います。


SkinnedMesh


スキンメッシュの表示

前回からチャレンジしていて、助言を元に色々と試行錯誤してました。

で、最終的にはコンバータから出力した頂点データが 固定機能のインデックス付き頂点データで、
表示させようとしている側は、シェーダーによる表示方法を選択。という不一致が原因だった模様。

コンバータから出力するデータも若干おかしいと思える箇所を SkinnedMeshサンプルを参考にして修正。
表示側も固定機能による表示方法(D3DINDEXED)を適用することでやっと表示できました。

次はシェーダーによる表示方法にチャレンジしたいと思います。ConvertToIndexedBlendedMeshによる変換作業が具体的に何をやっているかが謎ですね。固定機能にしろシェーダーにしろ特に中身のデータが変わるとは思えないのですが。

追加

シェーダー定数の個数制限のためにパレット制限がかかるということに気付きました。

基本的に26個のマトリックスしかシェーダーに転送できず、
そのためにConvertToIndexedBlendedMeshはデータを変換して、
ボーンコンビネーションを複数回に分割するということをしている模様。

tiny.xの例で言えば、固定機能時はパレット36個は1回のdrawコールでOKだったものが、シェーダーを使うと2回の描画に分かれる。コンビネーション数が2回になってました。

この分け方のアルゴリズムとかは興味ありつつも、調査はしないことにします。
とりあえずもっと先へ進みたいので。

その前にコンバーターのソースを整形しないと不味そうですが…。リークもあるし。


Xファイル解析


前回の反省点

前回のフォント問題は解決できたので、今回はDirectXのモデルファイルについて色々と。

ちなみにフォントの問題で一番引っかかったのが、
SHIFTJIS文字列→UNICODE変換時で変換できなかった文字について、
UNICODEでの”・”に変換されたため、除去に失敗してた…。

モデルフォーマット

今作っているライブラリ専用モデルファイルを作成しようと色々考え中。

DirectXのXファイルを読み込んで、自分の形式にするコンバータ作らないとなぁと思って、まずはそれを準備してみようと思って作業中。よってスキニングの仕組みを改めて調査&勉強してみたり。

スキニングモデルと単なるポリゴンメッシュのモデル、そしてアニメーション付き、
これらをちゃんと読み込んで変換するのはちょっと苦労しそうだ…。

アニメのことを考えると D3DXLoadMeshFromXは使えない。
D3DXLoadSkinMeshFromXofを使用するのか?と思いきや、LPDIRECTXFILEDATAは廃止に向かってるようだし、関数名変わってるし。

素直にアニメーションスキニングメッシュ読み込むサンプルをそのまま実装して、必要なデータだけ抽出するようにするか…。


インスタンシング


今日もインスタンシング

昨日に引き続き、今日はShaderInstancingをトライしていた。しかしHWインスタンシングに比べて動作は理解しやすい。単に形状データを繰り返し、頂点バッファを満たしておけばいい。あとはそれに対する変形パラメータを外部から叩き込めば終わり。必要回数、必要ポリゴン数になるまでdrawPrimitiveを繰り返す。

そんなわけで、さっくり実装できた。

HLSL

今は起動時で読み込んでコンパイルさせているけど、デフォルトのシェーダーくらいはlibに組み込んでしまいたいと思って色々調べてみた。どうやらバイナリのリソースとしてリソーススクリプトで記述すれば良さそうです。

GUIの操作としては、リソースのウィンドウで追加→ファイルから〜
その時の.rcの中身にはこんな風にして追記されることになります。

(グループ名を入れたかったのでそこだけは手作業)

<名前> <グループ名> DISCARDABLE “ファイル名.fx”

ここまでやって気付いたのだが、そのままバイナリファイルに組み込まれるみたい…。

さらに調べてみると .fxから .fxoを作ってしまうという方法もあるようだ。コンパイル済みシェーダーといったところか。

こちらを組み込みにすれば、先ほどよりはマシだろう。読まれたくないならば、
fxoをさらに暗号化してリソースにインポートすればいい話だし。

これには、プロジェクトに追加されている.fxファイルにコンパイルの記述を追加する。

こんな感じでコマンドライン行に追加。最初のはバージョンを決めるためのものだろう。
さらに、出力ファイルの箇所で “bin/$(InputName).fxo” と記述するのも忘れずに。

デバッグ版とリリース版で同じでも問題ないと思うけど、オプションを変更することも可能です。