ようこそゲストさん

すらりん日記

メッセージ欄

分類 【プログラミング::DirectX】 で検索

一覧で表示する

2007/01/08(月) インスタンシング

今日もインスタンシング

昨日に引き続き、今日はShaderInstancingをトライしていた。

しかしHWインスタンシングに比べて動作は理解しやすい。単に形状データを繰り返し、頂点バッファを満たしておけばいい。

あとはそれに対する変形パラメータを外部から叩き込めば終わり。

必要回数、必要ポリゴン数になるまでdrawPrimitiveを繰り返す。

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

HLSL

今は起動時で読み込んでコンパイルさせているけど、デフォルトのシェーダーくらいはlibに組み込んでしまいたいと思って色々調べてみた。

どうやらバイナリのリソースとしてリソーススクリプトで記述すれば良さそうです。

GUIの操作としては、リソースのウィンドウで追加→ファイルから〜

その時の.rcの中身にはこんな風にして追記されることになります。

(グループ名を入れたかったのでそこだけは手作業)
<名前> <グループ名> DISCARDABLE "ファイル名.fx"

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

さらに調べてみると .fxから .fxoを作ってしまうという方法もあるようだ。

コンパイル済みシェーダーといったところか。

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

で、プロジェクトに追加されている.fxファイルにコンパイルの記述を追加する。
 fxc /T fx_2_0 /Fo "bin/$(InputName).fxo"  "$(InputPath)"
こんな感じでコマンドライン行に追加。最初のはバージョンを決めるためのものだろう。

さらに、出力ファイルの箇所で "bin/$(InputName).fxo" と記述するのも忘れずに。

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

2007/04/15(日) SkinnedMesh

スキンメッシュの表示

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

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

という不一致が原因だった模様。

コンバータから出力するデータも若干おかしいと思える箇所を SkinnedMeshサンプルを参考にして修正。

表示側も固定機能による表示方法(D3DINDEXED)を適用することでやっと表示できました。

次はシェーダーによる表示方法にチャレンジしたいと思います。


ConvertToIndexedBlendedMeshによる変換作業が具体的に何をやっているかが謎ですね。

固定機能にしろシェーダーにしろ特に中身のデータが変わるとは思えないのですが。

追加

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

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

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

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

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

2007/04/17(火) メモリリーク追跡

DirectXのメモリリーク

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

そういうとき、DirectXのデバッグランタイムを使用しているのであれば、
以下のようなメッセージがプログラム終了時に表示されます
 Direct3D9: (WARN) :Memory still allocated!  Alloc count = 148
 Direct3D9: (WARN) :Current Process (pid) = 00000d8c
 Direct3D9: (WARN) :Memory Address: 00397368 lAllocID=1 dwSize=000047f8, (pid=00000d8c)
 Direct3D9: (WARN) :  Stack Back Trace
 Direct3D9: (ERROR) :    [0] : Address 0104D4CB Direct3D9: (ERROR) :    [1] : Address 0104D59B        以下略〜
こういうときに、実はデバッグランタイムを使用していればどこのタイミングで確保したモノなのかがチェックできるらしいです。

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

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

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

よって、数が大きいモノ、最後に参照カウンタをいじって生き残っているモノからデバッグ(解放処理?)を入れていくのは正しいのではないかと思います。
右記に記した赤丸の部分は、Break on AllocID という箇所で、ここに上記で表示されていたIDをセットすることで、そのタイミングでブレークをかけることが出来ます。また、Break on MemoryLeaksでチェックをいれておけば、終了時のタイミングでリークがあればデバッガで停止します。{{image 0, 'DXダイアログ', nil, [160,185]}}

2007/05/01(火) DirectX10 (D3D10)

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

まずは2007Apr版のDXSDKをダウンロードして、サンプルを実行してみてました。

DirectX10がどう動いてくれるか、わくわくしながらサンプルを実行したのですが、

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

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


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

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

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

Tutorial01解説もどき

自分のメモも含めて少し解説。DX9の頃と違って、かなり初期化周りも変更になっている。

Tutorial01.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 );
どうやら、スワップチェインを作成を作成した後、そのスワップチェインの中にあるバッファデータを取得。

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

Render()処理

 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 );
これをみても明らかなように、レンダーターゲットに対してクリアしてます。

今まではPresentもpd3ddeviceでしたが、今回からはPresentはスワップチェインに対して操作しています。

より明確に分かれた感じがしますね。
とりあえずTutorial01では、このくらいしか従来と大きく変わった部分は無さそうです。

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

2007/05/02(水) DirectX10 (D3D10)

Tutorial02解説

今回は、DX10での三角形(ポリゴン)描画までのサンプルを読み解いてみました。

前回までの初期化にプラスして、頂点データの作成、シェーダーの作成と要素が詰まってます。

シェーダーの作成は以下のようになってました。

若干自分なりのコメントをつけてみました。

間違っている場合もあると思いますが、気づいた人はコメントでご指摘ください。(^^;
 // 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 );
どうやらDX9時代と変更がない感じです。プロファイルが fx_4_0になったくらいでしょうか。
シェーダーのフラグでDX10用のHLSLを要求しているように見えます。

fxファイルを覗くとわかるのですが、technique10や今までに見慣れないセマンティクスが使われています。
これらを要求するためのフラグみたいです。
 // 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 );
シェーダーのバイトコードと、頂点宣言二つからinputlayoutを作成します。
そしてこのレイアウトはIA(InputAssembler)にセット。
この次に、ようやく頂点バッファの作成に取りかかります。

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

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

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

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

SubResourceとはなんぞや?

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

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

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

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

2007/05/04(金) DirectX10 (D3D10)

Tutorial04

今回はTutorial04を見てました。

このサンプルは立方体(キューブ)の表示サンプルでした。

前回のTutorial02の部分に比べて増えたところと言えば、IndexBufferを作っているところです。

ほかには、シェーダーで透視変換させているところ。

大きく変わった部分はないです。

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

変更になってる部分はデータをどう使うか指示するところ、

「D3D10_BIND_INDEX_BUFFER」「D3D10_BIND_VERTEX_BUFFER」の違いくらいです


描画する前には以下のように
 // Set index buffer
 g_pd3dDevice->IASetIndexBuffer( g_pIndexBuffer, DXGI_FORMAT_R32_UINT, 0 );
 // Set Vertex buffer
 g_pd3dDevice->IASetVertexBuffers( 0, 1, &g_pVertexBuffer, &stride, &offset );
 // Set primitive topology
 g_pd3dDevice->IASetPrimitiveTopology( D3D10_PRIMITIVE_TOPOLOGY_TRIANGLELIST );
各バッファをデバイスに設定し、プリミティブタイプを指定しておきます。

この辺が若干以前と変わっています。

前はDrawPrimitive系でプリミティブタイプを指定していました。

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

気になったのはこのサンプル、インデックスに32ビット指定してます。
従来は16ビット(WORD型)を基本的に使っていたのに。
デバイスの最適化もこれからは32ビットインデックスが主流となっていくんですかねぇ…。
※ 現状のデバイスは16ビットインデックスに最適化されています
 // Update variables
 //
 g_pWorldVariable->SetMatrix( (float*)&g_World );
 g_pViewVariable->SetMatrix( (float*)&g_View );
 g_pProjectionVariable->SetMatrix( (float*)&g_Projection );
 
 D3D10_TECHNIQUE_DESC techDesc;
 g_pTechnique->GetDesc( &techDesc );
 for( UINT p = 0; p < techDesc.Passes; ++p )
 {
   g_pTechnique->GetPassByIndex( p )->Apply(0);
   g_pd3dDevice->DrawIndexed( 36, 0, 0 );   // 36 vertices needed for 12 triangles in a triangle list
 }
シェーダーに対して、各マトリックスをセットして、パスを回しています。

見てわかるように描画のDrawIndexedの引数が減っています。すっきりしましたね〜。

あとDX9世代でBegin/BeginPassで行っていたパスを回す処理が変更になっています。

テクニックのDescから値を取得して、回数と使用指示を設定しているようです。


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

気になったのは今までシェーダー定数を書き換えたときには、

CommitChangesなどのメソッドを呼んで確定させていた気がしますが、
それが消えています。

シェーダーコード

HLSLの中身も若干変わっています。
 //------------------------------
 // Constant Buffer Variables
 //------------------------------
 matrix World;
 matrix View;
 matrix Projection;
今までfloat4x4とかやっていた部分がmatrixになっています!

Passの部分も
 pass P0
 {
     SetVertexShader( CompileShader( vs_4_0, VS() ) );
     SetGeometryShader( NULL );
     SetPixelShader( CompileShader( ps_4_0, PS() ) );
 }
となっており、ぱっとみてわかる程度の変更です。

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

2009/08/31(月) SkinnedMeshで複数描画したい

DirectXのサンプルSkinnedMeshをいじって、
リソースは1つ、描画用インスタンスは複数なんてものを作成してみました。

ss1.png

色々と、DirectXの仕様で躓く点が多かったように思います。

実現の為のポイント

  • AnimationControllerは単純にCloneで作成しても、ボーン構造(FRAME)は共有されてしまう
    • 独立させたいのは、マトリックスパレット.
    • 一緒にボーンオフセット行列もインスタンスごとに保持させてしまうと、楽にはなる。
  • マトリックスパレットは、MeshContainerが保持するのではなく、一段外に追いやってみる。
    • 自分では FRAMEの中に追い出しました。MeshContainerと並列して存在する感じに。
このような点から、
描画用のオブジェクト生成時には、
  • FRAME構造をコピー(別インスタンス作成)
  • AnimationControllerをcloneする
  • 上記でそれぞれ描画用のインスタンス用としてのデータができたので、関連づける.
    • D3DXFrameRegisterNamedMatrices()を使用する
これで画像のようにインスタンスごとにアニメーションデータを持った感じで
描画ができるようになりました。

面倒といえば面倒なので、要望があったらlib化してしまうかも。

2009/09/27(日) マニュアル間違ってる…

DirectX SDKのヘルプで、
D3DXSaveMeshHierarchyToFileの部分で、形式設定が間違ってた。
  • D3DXF_FILEFORMAT_BINARY
  • D3DXF_FILEFORMAT_TEXT
が使用可能。

DXFILEFORMAT_BINARY って書いてあったけど、こんな定義がどこにも見あたらない・・・。

それとこの関数使うとメモリがリークしてるんですが、どう解消すればいいかがさっぱり。

2009/11/23(月) 今日のトラブル

原因不明。

IDIRECT3DDEVICE9のSetVertexShaderConstantF関数で設定した値が、シェーダーに送られてない。
一方で、D3DXのID3DXCONSTANTTABLEで設定すると値はきちんと送られている。

なぜだー。


これのせいで数時間もアルゴリズムの方が間違っているのかと悩んでた。

現在ダウンサンプリングを勉強中

2010/04/18(日) DirectXのデバッグランタイム不具合

Windows7 (x64)の環境で、DirectX9を用いた開発を行おうとすると、
SDKのバージョンによっては問題を引き起こすことがわかった。

検索しても海外でしか、同じ症状が発見されず、また解決法も不明だったので、
問題のあるバージョンを使用しない方法でしか解決が出来ないようだ。
それでも、別バージョンを使えばOKという点はまだ救いなのかもしれない。

問題

Windows7(x64)において、DirectX9のデバッグランタイムを有効にすると、
CreateDeviceで失敗する。
そのときのエラーメッセージは次の通り。
Direct3D9: (ERROR) :The specified adapter does not support a D3D9 hardware device
Direct3D9: (ERROR) :Failed to initialize Framework Device. CreateDeviceEx Failed.
コメントもらったので修正
試したのは全て、Windows7(x64)環境下です。
x86環境では問題が起こらないのかもしれません。
どうやらx86,x64環境ともに発生するようです。
SDKバージョン使用可否Windows仕様
2008Nov使用不可能Win7(x64)
2009Mar使用不可能Win7(x64)
2009Aug使用可能Win7(x64)
2010Feb使用可能Win7(x64)

まとめ

このような結果になったので、Windows7環境でまともに使おうと思っても、
古すぎるSDKは使いにくい。2009Augがいいところだろうか。

1: てぃあの 『当方Win7の32Bitですが、2008Novで同じ状況でした。 ご報告まで。 新しいDirectXSDKにするしかないようですね。』 (2010/06/16 16:27)

2: slash 『> てぃあのさん コメントありがとうございます。 そうですか、Win7 32bit版でも症状は変わりませんでしたか。 Windows7においては、まだ歴史が浅いですし、 ベストなSDKを探っていこうと思います。』 (2010/06/18 20:34)