[PR]
[PR]上記の広告は3ヶ月以上新規記事投稿のないブログに表示されています。新しい記事を書く事で広告が消えます。
プログラミング、3DCGとその他いろいろについて
[PR]上記の広告は3ヶ月以上新規記事投稿のないブログに表示されています。新しい記事を書く事で広告が消えます。
さっそくDirect3D10のデバイスを作って、ウィンドウのクライアントエリアを(青く)クリアしてみました。
デバイスを作るといっても、残念ながらぼくはDirectX10対応のグラフィックスカードを持っていないので、(リファレンス・デバイスを使って)ソフトウェアで動作をエミュレートすることにします。
解説はややこしくなりそうなので、まずはコードと結果ををのせます。
Dx10Test.cpp
#using<System.Windows.Forms.dll> #using<System.dll> #include<d3d10.h> using namespace System; using namespace System::Windows::Forms; ref class Game : Form { //描画を行うデバイスです。 //これを使ってポリゴンとかいろいろなものを描画します。 ID3D10Device* graphicsDevice; //スワップ・チェーン //これはダブルバッファリングを行うための2つのバッファを持っています。 //(片方が描画対象、もう片方はディスプレイに表示されるバッファ) //この2つのバッファは、描画が終わって実際にディスプレイに表示するときに //役割が交代(スワップ)します。 //また、実際には2より多くのバッファが鎖(チェーン)のように //続くように設定することもできます。 IDXGISwapChain* swapChain; //描画する対象です。 //画面をある一色で塗りつぶ(クリア)したいときには //これに対してクリアします。 ID3D10RenderTargetView* renderTargetView; public: void Run() { InitDevice(); Show(); while(Created) { Draw(); Application::DoEvents(); } CleanupDevice(); } private: void InitDevice() { createGraphicsDevice(); createRenderTargetView(); setupViewport(); } void CleanupDevice() { if(graphicsDevice) graphicsDevice->ClearState(); if(renderTargetView) renderTargetView->Release(); if(swapChain) swapChain->Release(); if(graphicsDevice) graphicsDevice->Release(); } void Draw() { float blue[] = {0, 0, 1, 1}; graphicsDevice->ClearRenderTargetView(renderTargetView, blue); swapChain->Present(0, 0); } void createGraphicsDevice() { DXGI_SWAP_CHAIN_DESC swapChainDescription; ZeroMemory(&swapChainDescription, sizeof(swapChainDescription)); swapChainDescription.BufferCount = 1; swapChainDescription.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; swapChainDescription.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; swapChainDescription.OutputWindow = (HWND)(this->Handle.ToInt32()); swapChainDescription.SampleDesc.Count = 1; swapChainDescription.Windowed = TRUE; //C++/CLIでは、クラスのメンバー変数のポインタを //直接&graphicsDeviceとか&swapChain //というふうにして関数に渡すことができません。 //一方、ローカルな変数ならそれができます。 //そこでここでは間接的に、このクラスのメンバー、 //graphicsDeviceとswapChainに値をセットすることにします。 ID3D10Device *graphicsDevice; IDXGISwapChain *swapChain; HRESULT result = D3D10CreateDeviceAndSwapChain( NULL, D3D10_DRIVER_TYPE_REFERENCE, NULL, 0, D3D10_SDK_VERSION, &swapChainDescription, &swapChain, &graphicsDevice ); if( FAILED(result) ) throw gcnew Exception("Couldn't create Graphics Device"); this->graphicsDevice = graphicsDevice; this->swapChain = swapChain; } void createRenderTargetView() { ID3D10Texture2D *backBuffer; HRESULT result = swapChain->GetBuffer(0, __uuidof(ID3D10Texture2D), (LPVOID*)&backBuffer); if( FAILED(result) ) throw gcnew Exception("Couldn't get BackBuffer from swap chain."); ID3D10RenderTargetView* renderTargetView; result = graphicsDevice->CreateRenderTargetView(backBuffer, NULL, &renderTargetView); backBuffer->Release(); if( FAILED(result) ) throw gcnew Exception("Couldn't create RenderTargetView."); this->renderTargetView = renderTargetView; } void setupViewport() { D3D10_VIEWPORT viewport; viewport.Width = Width; viewport.Height = Height; viewport.MinDepth = 0.0f; viewport.MaxDepth = 1.0f; viewport.TopLeftX = 0; viewport.TopLeftY = 0; graphicsDevice->RSSetViewports( 1, &viewport ); } }; int main() { Game ^game = gcnew Game(); game->Run(); }
青くウィンドウのクライアント領域が塗りつぶされました。
成功です。
次回にでもこの上にポリゴンを描いてみましょうかね。
ここからDirectX10を使っているのでコンパイルにはd3d10.libが必要です。
cl /clr dx10Test.cpp d3d10.lib
大まかな説明
大まかな流れは次のようになります。
1.ID3D10Device、IDXGISwapChain、ID3D10RenderTargetViewオブジェクトをつくっていろいろ設定を行います。
2.(メッセージループ中に)ウィンドウのクライアント領域を青でクリア。この作業には1で作った3つのオブジェクト全部を使います。
3.クリーンナップ。 「1.」の後始末。
ID3D10DeviceとはDirect3D10で描画に使うハードウェア(ビデオカード)の描画機能をあらわすインターフェースです(ただし!ハードウェアを使わずにソフトウェアで機能をエミュレートすることもできます。そうした場合実行速度が遅くなりますが、DirectX10対応のビデオカードが無くても動きます)。
描画を行うメソッドはこのインターフェースが持っています。
今回はやりませんが、ポリゴンとかを描画するのに使うのはこのインターフェースです。
IDXGISwapChainは、乱暴に説明するなら、Direct3D10でダブルバッファリングを行うためのインターフェースです。
ダブルバッファリング――つまりプログラムで描画するバッファ(バック・バッファ)と、実際にディスプレイ画面に描画されるバッファ(フロント・バッファ)の2つに分かれています。
このインターフェースは多くの場合この2つのバッファを持つことになります(しかし、3つ以上にすることも可能です。そうした場合、バッファが"鎖"(チェーン)のようにつながっているように見えることがわかると思います)。
プログラムはバック・バッファに対して描画を行います。
そして、描画が完了するとそのバック・バッファを今度はフロント・バッファに変えます(そうすると描画したものがディスプレイ画面に表示されるようになりますからね)。
同時に、それまでフロント・バッファだったものは今度はバック・バッファに変えます。
つまり、バック・バッファとフロント・バッファの役割を交代(スワップ)することによって、描画結果を画面に反映させているのです。(まるではたおり機で布をバッタンバッタンと織っていくような感じですね。あれは縦糸を上下の2つのグループに分けて、横糸を通したらこの2つのグループを入れ替えて…というふうにして布を織っていくのです。)
バッファ1 | バッファ2 | |
1回目のDraw | バック・バッファ(プログラムが描画を行う) | フロント・バッファ(ディスプレイに表示される) |
2回目のDraw | フロント・バッファ | バック・バッファ |
3回目のDraw | バック・バッファ | フロント・バッファ |
HRESULT D3D10CreateDeviceAndSwapChain( IDXGIAdapter *adapter, D3D10_DRIVER_TYPE driverType, HMODULE softwareRasterizerDll, UINT deviceCreationFlags, UINT sdkVersion, DXGI_SWAP_CHAIN_DESC *swapChainDescription, IDXGISwapChain **outSwapChain, ID3D10Device **outDevice );
typedef enum D3D10_DRIVER_TYPE { D3D10_DRIVER_TYPE_HARDWARE = 0, D3D10_DRIVER_TYPE_REFERENCE = 1, D3D10_DRIVER_TYPE_NULL = 2, D3D10_DRIVER_TYPE_SOFTWARE = 3 } D3D10_DRIVER_TYPE;
説明 | |
HARDWARE | ハードウェア(ビデオカード)を使ってID3D10Deviceを作ります。実用向け。 |
REFERENCE | ソフトウェアでDirect3D10対応ビデオカードをエミュレートします。そのためDirect3D10対応ビデオカードが無くってもデモを動かすことが出来て便利です(でも遅い)。 |
NULL | 実際の描画能力のないデバイスを作ります。 |
SOFTWARE | 将来のために予約されています。 |
typedef enum D3D10_CREATE_DEVICE_FLAG { D3D10_CREATE_DEVICE_FLAG_SINGLETHREADED = 0x1, D3D10_CREATE_DEVICE_FLAG_DEBUG = 0x2, D3D10_CREATE_DEVICE_FLAG_SWITCH_TO_REF = 0x4, D3D10_CREATE_DEVICE_FLAG_PREVENT_INTERNAL_THREADING_OPTIMIZATIONS = 0x8 } D3D10_CREATE_DEVICE_FLAG;
説明 | |
SINGLETHREADED | デバイスのスレッドセーフティを無効にします。(Direct3D10ではデフォルトでスレッドセーフ) |
DEBUG | デバッグレイヤーをデバイスに追加します。 それによってエラーレポートの文字列を出力(ID3D10InfoQueueを使う)できるようになったりします。 |
SWITCH_TO_REF | デバイスがハードウェアかリファレンスかを変えることができるようになります(ID3D10SwitchToRef::SetUseRefを使う)。 |
PREVENT_INTERNAL_THREADING_OPTIMIZATIONS | 予約されています。 |
typedef struct DXGI_SWAP_CHAIN_DESC { DXGI_MODE_DESC BufferDesc; DXGI_SAMPLE_DESC SampleDesc; DXGI_USAGE BufferUsage; UINT BufferCount; HWND OutputWindow; BOOL Windowed; DXGI_SWAP_EFFECT SwapEffect; UINT Flags; } DXGI_SWAP_CHAIN_DESC;
BufferDescはスワップチェーンがどのようなバッファを持つかを表します。
バッファの大きさやフォーマット、拡大する時にはどのように拡大するかといったことを表します。
BufferDescの型はディスプレイのモードを表すDXGI_MODE_DESC構造体です。
typedef struct DXGI_MODE_DESC { UINT Width; UINT Height; DXGI_RATIONAL RefreshRate; DXGI_FORMAT Format; DXGI_MODE_SCANLINE_ORDER ScanlineOrdering; DXGI_MODE_SCALING Scaling; } DXGI_MODE_DESC, *LPDXGI_MODE_DESC;
WidthとHeightは解像度の横幅と縦幅です。
RefreshRateは一秒間にリフレッシュする回数を表します。この型は分数(Rational)を表すDXGI_RATIONAL構造体で、2つのUINT型のメンバー、Numerator(分子)とDenominator(分母)を持っています。MSDNのチュートリアルを見る限りでは分子を60、分母を1にセットするのが普通なようです(つまり1秒間に60回リフレッシュ)。でもそれじゃあ構造体なんて使わなくって、ただのUINTでいいような気も……。
typedef struct DXGI_RATIONAL { UINT Numerator; UINT Denominator; } DXGI_RATIONAL, *LPDXGI_RATIONAL;
Formatは、データのフォーマットを表します。
この型はDXGI_FORMAT列挙型です。
メンバーを紹介したいのですが、死ぬほど多い(90個)のでリンクだけ。
メンバー名を見たところ、「DXGI_FORMAT_[データのレイアウト]_[型]」という法則があるようですね(例えば、DXGI_FORMAT_R32G32B32_FLOATと言った具合です)。
ScanlineOrderingはラスターがイメージを作り出す方法です。この型はDXGI_MODE_SCANLINE_ORDER列挙型です。
定数名 説明 値 DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED 順番を指定しません。 0 DXGI_MODE_SCANLINE_ORDER_PROGRESSIVE 最初から最後まで順番に、スキップすることなく。 1 DXGI_MODE_SCANLINE_ORDER_UPPER_FIELD_FIRST 上のほうのフィールドから始めます。 2 DXGI_MODE_SCANLINE_ORDER_LOWER_FIELD_FIRST 下の方のフィールドから始めます。 3
Scalingはどのようにイメージを拡大するかを表します(指定されたモニターの解像度に合わせなければいけませんからね)。
この型はDXGI_MODE_SCALING列挙型です。
定数名 説明 値 DXGI_MODE_SCALING_UNSPECIFIED 拡大の方法を指定しません 0 DXGI_MODE_SCALING_CENTERED 拡大しません。イメージはディスプレイの中央に表示されます。 1 DXGI_MODE_SCALING_STRETCHED 引き伸ばされて拡大されます。 2
さて、DXGI_SWAP_CHAIN_DESC構造体のメンバーの説明に戻ります。(こういうネストっていやですね!)
次はSampleDescです。
SampleDescは、このスワップチェーンでアンチ・エイリアシング(画像のギザギザを無くして滑らかに)する方法をあらわします。
アンチエイリアシングするにはいくつかの点の色を採ってきて(サンプルする)、その平均値の色を使います。
このメンバの型はそのマルチ・サンプリングを表すDXGI_SAMPLE_DESC構造体です。
typedef struct DXGI_SAMPLE_DESC { UINT Count; UINT Quality; } DXGI_SAMPLE_DESC, *LPDXGI_SAMPLE_DESC;
Countはピクセル1つあたりのマルチサンプルの数です。デフォルトでは1で、アンチエイリアシングを行いません。
Qualityはイメージのクオリティレベルです。
値の範囲は0 ~ (ID3D10Device::CheckMultisampleQualityLevelsの戻り値 - 1)までです。
デフォルトでは0です。
説明 | |
DXGI_USAGE_SHADER_INPUT | シェーダーへの入力として使います。 |
DXGI_USAGE_RENDER_TARGET_OUTPUT | レンダーターゲット、つまりディスプレイへの出力として使います。 |
DXGI_USAGE_BACK_BUFFER | バックバッファとして使います。 |
DXGI_USAGE_SHARED | 共有します。 |
DXGI_USAGE_READ_ONLY | 読み込み専用です。 |
typedef enum DXGI_SWAP_EFFECT { DXGI_SWAP_EFFECT_DISCARD = 0, DXGI_SWAP_EFFECT_SEQUENTIAL = 1 } DXGI_SWAP_EFFECT, *LPDXGI_SWAP_EFFECT;
typedef enum DXGI_SWAP_CHAIN_FLAG { DXGI_SWAP_CHAIN_FLAG_NONPREROTATED = 1, DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH = 2 } DXGI_SWAP_CHAIN_FLAG, *LPDXGI_SWAP_CHAIN_FLAG;
説明 | |
DXGI_SWAP_CHAIN_FLAG_NONPREROTATED | これはフルスクリーンモードでしか意味を成さないのですが、自動的なイメージの回転をオフにします。つまり、ゲームが回転して表示しなきゃいけないような時に、フロントバッファからモニターへ表示する時の回転をしません。 |
DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH | ディスプレイモード(DXGI_MODE_DESC)を途中で変えることができることを表します(変えるにはIDXGISwapChain::ResizeTargetを使います)。 |
HRESULT GetBuffer( UINT bufferIndex, FEFIID bufferInterfaceType, void **outBackBuffer );
HRESULT CreateRenderTargetView( ID3D10Resource *resource, const D3D10_RENDER_TARGET_VIEW_DESC *description, ID3D10RenderTargetView **outRenderTargetView );
void RSSetViewports( UINT numViewports, const D3D10_VIEWPORT *viewports );
typedef struct D3D10_VIEWPORT { INT TopLeftX; INT TopLeftY; UINT Width; UINT Height; FLOAT MinDepth; FLOAT MaxDepth; } D3D10_VIEWPORT;
TopLeftXとTopLeftYは、描画する領域の左上の点を表します。(こんな変数名にしないで単にX, Yでいいような気もするのですが・・・)
Width、Heightは描画する領域のサイズを表します。
MinDepthとMaxDepthは深度バッファのとる値の範囲です。(深度バッファとはピクセルごとに用意される値で、そのピクセルのカメラからの距離を表します(ただし0から1まで)。これの値を使って、デバイスは近くのポリゴンを、遠くのポリゴンより優先して描画します。これがないと近くの壁が遠くのキャラクターの後ろに描画されると言うめちゃくちゃなことになります。)
これは両方とも0と1の間でなければなりません。
普通は単純に、MinDepth = 0、MaxDepth = 1としていいでしょう。
void ClearRenderTargetView( ID3D10RenderTargetView *renderTargetView, const FLOAT colorRGBA[4] );
HRESULT Present( UINT syncInterval, UINT flags );
void ClearState();