テクスチャ付きの板きれを表示するまで

DirectX11で板きれモデルを表示するプログラムを作ってみた。ハマリまくって、ようやく画面いっぱいにテクスチャ画像が表示できるところまで進んだので整理も兼ねてメモ。


まずは今回のバッドノウハウから。
○バックバッファのフォーマットはDXGI_FORMAT_R8G8B8A8_UNORMらしい
 →そもそもUNORMって何?
 →とにかくテクスチャとディスプレイでは使えるフォーマットが違うっぽい
 →DXCapsViewer.exeであらかじめディスプレイフォーマットを調べておくと良い


○DXGI_SAMPLE_DESC::Countはマルチサンプルしないときでも「1」
 →Count=1、Quality = 0 に設定するっぽい


○DXGI_SWAP_CHAIN_DESC::OutputWindowに指定するウィンドウは自分で作ったウィンドウだけ
 →FindWindowExで取ってきたHWNDを指定するとD3DERR_INVALIDCALLが返る


○D3D11_VIEWPORT::MinDepth、MaxDepthは0.0〜1.0の範囲
 →通常はMin=0.0、Max=1.0固定
 →範囲外の値に設定するとモデルが表示されない


○D3D11_TEXTURE2D_DESC::ArraySizeは通常「1」
 →てか、texture arrayってなんぞ?


○D3D11CreateDeviceAndSwapChain()関数はバックバッファをRenderTargetViewに登録してくれない
 →IDXGISwapChainからバッファバッファをもらって自分で登録する必要がある


[2010/7/19 23:55追記]
○cbufferは行と列が逆転する?
 →PIX先生で見たときにcbufferとシェーダ側のc0レジスタ値がひっくり返っている
 →シェーダ側でrow_major指定するかあらかじめ転置行列を与えるかする必要がある
 →row_major指定するとアセンブリ命令数が増える気がする(ぱっと見た感じでは。きちんと比較していない)



以下は板きれ表示するだけの処理で必要だった処理(メソッド名をピックアップ)

まずは初期化編。この辺の処理は普通最初に1回だけ呼ぶはず。

// ID3D11Device、ID3D11DeviceContext、IDXGISwapChain を作成
// IDXGISwapChain は VSYNC あり表示に必要
D3D11CreateDeviceAndSwapChain()

// CullModeとかをいじくるのに必要。いじらないときは不要?
ID3D11Device::CreateRasterizerState()
ID3D11DeviceContext::RSSetState()

// ビューポートの設定
ID3D11DeviceContext::RSSetViewports()

// 深度バッファとDSV(DepthStencilView)を用意
ID3D11Device::CreateTexture2D(&DepthBuffer)
ID3D11Device::CreateDepthStencilView(DepthBuffer)

// SwapChainからバックバッファをもらってRTV(RenderTargetView)を用意
IDXGISwapChain::GetBuffer(0, __uuidof(ID3D11Texture2D), reinterpret_cast(&BackBuffer))
ID3D11Device::CreateRenderTargetView(BackBuffer)

// DSVとRTVを登録
ID3D11DeviceContext::OMSetRenderTargets()

次が個別ロード編。モデルをロードするときに必要になる。

// VertexShaderとInputLayoutの用意
D3DX11CompileFromFile("vs.hlsl")
ID3D11Device::CreateInputLayout()
ID3D11Device::CreateVertexShader()

// PixelShaderの用意
D3DX11CompileFromFile("ps.hlsl")
ID3D11Device::CreatePixelShader()

// VertexBuffer、IndexBuffer、constant buffer(シェーダへのパラメータ受け渡し用)を用意
// Vertex、IndexBufferはCreateする際にファイルから読み込んだモデルデータを初期値として与える
ID3D11Device::CreateBuffer(&VertexBuffer)
ID3D11Device::CreateBuffer(&IndexBuffer)
ID3D11Device::CreateBuffer(&VS用cbuffer)
ID3D11Device::CreateBuffer(&PS用cbuffer)

// 必要なテクスチャの読み込み
// SamplerState(シェーダがテクスチャをreadするときの設定?)と
// テクスチャのSRV(ShaderResourceView)を用意
ID3D11Device::CreateSamplerState()
D3DX11CreateShaderResourceViewFromFile()

最後がレンダリング編。これは毎フレーム実行する。

// DSVとRTVをクリア
// 全Pixelを書き替えることがわかっているならRTVはクリアしなくても良いかも
ID3D11DeviceContext::ClearDepthStencilView()
ID3D11DeviceContext::ClearRenderTargetView()

// 最新のカメラ位置から変換行列を計算
D3DXMatrixLookAtLH()
D3DXMatrixPerspectiveFovLH()

// PSへのパラメータ設定
ID3D11DeviceContext::Map(PS用cbuffer)
// cbufferにカメラ位置、光源の位置を設定する
ID3D11DeviceContext::Unmap()


// 以降はモデルの部位ごとに切り替え

// VSへのパラメータ設定
ID3D11DeviceContext::Map(VS用cbuffer)
// cbufferにローカル座標→投影座標変換用行列を設定する
ID3D11DeviceContext::Unmap()

// レンダリングパイプの設定
ID3D11DeviceContext::IASetPrimitiveTopology()
ID3D11DeviceContext::IASetInputLayout()
ID3D11DeviceContext::IASetIndexBuffer()
ID3D11DeviceContext::IASetVertexBuffers()
ID3D11DeviceContext::VSSetConstantBuffers(VS用cbuffer)
ID3D11DeviceContext::VSSetShader()
ID3D11DeviceContext::PSSetConstantBuffers(PS用cbuffer)
ID3D11DeviceContext::PSSetShaderResources(テクスチャのSRV)
ID3D11DeviceContext::PSSetSamplers(テクスチャread用の設定)
ID3D11DeviceContext::PSSetShader()

// レンダーターゲットに出力
ID3D11DeviceContext::DrawIndexed()

// ここまでモデルの部位ごとに繰り返し


// バックバッファからウィンドウへコピー
IDXGISwapChain::Present()

てな感じでとりあえず表示できたけど、長い…なんでこんなにいろいろ呼び出しが必要なんだろ。まだEffectだって使ってないのに。
DirectX11になってデフォルトで設定されてるモノが減った気がする。今回みたいにモデルを表示したいだけならDirectX9でハードウェアお任せにした方が楽な希ガス

ちなみにデバッグ中にPIX先生にはとてもお世話になりました。PIX先生のF12キャプチャでDrawIndexedコールを見たときの「Details」ウィンドウの「Mesh」タブがヤバい。「PreVS」と「PostVS」で処理したポリゴンの座標変換前後の値が確認できるし、画面のどのあたりにいるのか「Viewport」欄で確認できるのがとても良い。設定がおかしいとそもそも「Viewport」欄に何も表示されなかったりしておかしいところを絞り込むのにとても役に立った。
こんな感じで確認できる。


「PostVS」タブ側で選択した頂点(黄色)が上側の「Pre-Vertex Shader」や「Viewport」画面でどのあたりの頂点なのか黄色で表示される。青色の部分はモデル全体でこれがViweportに移ってないとそもそも座標変換がおかしいってことになる。