どうも、星姫です。
今回はDirectX10を使って、3Dオブジェクトをモニタに表示します。
今回は3D描画ライブラリのDirectX 10に、どんな風に描画してもらうか情報を渡すだけ…なんですが
ちょっと設定項目が多すぎやしまんせんか?
そう思っているのは私だけ?
思えば3D数学を講座で扱おうと023話で3Dオブジェクトを表示しようとしたのが始まりでした。
これまで、3Dオブジェクトのデータを用意するためにBlenderから中間ファイルを出力し
その中間ファイルをバイナリデータに変換するコンバーターをC#で作成してきました。
え?Blenderのエクスポーターと、中間ファイルのコンバーターが欲しい?
それだけください!ってな方のためにDownloadページを設けました。
バグだらけですが(おいおい!)、使えそうと思うものはどんどん持っていってください。
さて本題に入ります。
まずは、変換したモデルデータ( .smplデータ)の読み込み方です。
入出力ストリームというのを使います。
こんな感じ↓
023話で示した
この部分を書き換えてみました。
いくつかこちらで定義した型を使っています。
それらを以下に示しますね。
また、SimpleConverterに頂点カラー情報を出力できる機能を追加しました。
こちらは、ファイルヘッダ情報を引き出すための構造体です。
ファイルデータのポインタをこれにキャストするだけで、簡単にファイル情報を聞き出せるようになります。
こちらは定数定義ですね。
無名の名前空間で囲うと、そのファイルの中だけで参照可能な定数を定義できます。
ファイル内で共有する定数は、こうした形で定義しましょう。
基本的に、グローバル定数は禁止です。
そうなると、チュートリアルのコードも修正しなければいけませんね。
こちらはデバッグ出力用の関数の定義です。
デバッグモード時に出力ウィンドウに設定した文字列と、ファイル名、行番号、関数名を出力します。
可変長引数とか、初心者には見慣れないものがあるけど、これprintf関数と同じ形式で使えるから便利ですよ。
一度書いたら、次回以降使いまわすものなので、めったに書くものではありません。
書き方を忘れてしまったら、ここに戻ってくるようにしましょう。
さて、ファイルからモデルデータの読み込みと、良く使うデバッグ出力関数の確認ができたところで
これまでコピペしてきた DirectX 10 の設定部分の詳細を見ていきましょう。
え?、一番新しい DirectX は 11 なのに、なぜ 10 を扱うのかって?
それは、私のマシンが Windows Vista だからです。
11 を使うには、Windows 7 以上かつ、それを扱えるグラフィックボードが必要になります。
いつの日か…私のマシンが DirectX 11 を使えるようになったら、扱ってみようと思います。(まだ、そんなお金ないよ!)
まず、はじめにDirectX デベロッパー センターから開発者用のドキュメントを手に入れましょう。
DirectX ドキュメント 日本語版
Windows DirectX グラフィック ドキュメント 日本語版
この2つくらいは、手に入るんじゃないでしょうか?
とりあえず、DirectX 10 とは? みたいな情報を一通り読んでみます。
…で、情報入力から画面出力までの流れについて、以下は最初に書かれている部分のほぼコピー&ペーストなのですが…
1.入力アセンブラー ステージ - パイプラインにデータ (三角形、線、およびポイント) を供給する
2.頂点シェーダー ステージ - 常に、単一の入力頂点を取り、単一の出力頂点を生成する
3.ジオメトリ シェーダー ステージ - 入力はプリミティブ全体である、入力プリミティブの破棄、または複数の新規プリミティブが出力される
4.ストリーム出力ステージ - メモリーにプリミティブ データを出力するように設計されている。メモリーに出力されたデータは、パイプラインに入力データとして読み込んで循環させるか、CPU から読める
5.ラスタライザー ステージ - ピクセル シェーダーの呼び出し方法の決定を実行する
6.ピクセル シェーダー ピクセル単位のデータ (カラーなど) を生成する
7.出力結合ステージ - 最終的なパイプラインの結果を生成する
…だそうです。
7つもステージがありますね、これ基本なので名前と順番は覚えないといけないみたいです。
DirectX 10 のチュートリアル05は、この7つのステージを書いているみたいなので、一つずつステージを追ってみましょう。
…と、その前にまだデバイス初期化の部分を明らかにしていませんでしたね。
そこから始めますか…
DirextXでウィンドウに描画するまでに必要なモノとして、まずグラフィックカードがあげられます。
PCで3Dゲームを楽しみたいという方は、一度はグラフィックカードのことを気にかけたことがあると思います。
DirectXはこのデバイス(装置)に描画の計算をさせるライブラリなので
まず、マシンが持つグラフィックカードへのアクセス子を作成するんです。
その時に使うのが次の関数です。
なんか「スワップチェインも」とか書かれているけど、同時に作るっぽいね。
DXGI_SWAP_CHAIN_DESC というディスクリプタ(設定)にバックバッファというモニタの裏側の情報を設定して
D3D10CreateDeviceAndSwapChain関数にディスクリプタを渡して使うみたい。
とりあえず、ここまでで作られるアクセス子は次の二つ
・ID3D10Device* pd3dDevice
・IDXGISwapChain* pSwapChain
次に作るのはこちら、レンダリングターゲット
・ID3D10RenderTargetView* pRenderTargetView
ややこしいかもしれないけど、スワップチェインが持つバッファをリソースとして取り出し
次のデバイスの関数を元に作成するんだって…ドキュメントにも書いてあったよ。
pd3dDevice->CreateRenderTargetView
pd3dDeviceはこんな感じで、あちこちで使われるけど pSwapChain は、これ以降レンダリング結果をモニタへ表示する
Present関数を呼ぶまで出番は無いようですね。
で、次に作るのが深度ステンシル
・ID3D10Texture2D* pDepthStencil
デバイスの pd3dDevice->CreateTexture2D 関数で作成してます。
また、深度ステンシルビューっていう次の
・ID3D10DepthStencilView* pDepthStencilView
もデバイスの pd3dDevice->CreateDepthStencilView 関数で作成してますね。
続いて、ここまでに作成してきたビュー2つ(pRenderTargetView, pDepthStencilView)
を出力結合ステージ関係で渡してなんか設定している様子
pd3dDevice->OMSetRenderTargets
さらに、今回のDirectX 10 から、やらなければいけない作業の一つである、ビューポートの設定を行ってました
具体的にはデバイスの pd3dDevice->RSSetViewports 関数を呼ぶだけです
さて、次にエフェクトをファイルから作成していますね。
ファイルとは、プロジェクトにぶら下がっている.fxファイルのことなんだけど、ちょっと中身を確認してみましょうか。
定数として、ワールドとビュー、プロジェクションの3つの行列が宣言されていて
頂点シェーダー
ピクセルシェーダー
パスが一つだけのテクニック…が書かれているのが分かります。
ここで、エフェクト、テクニックってなんでしょうね?
ここでいう何なのかとは、DirectXにおいて何を示しているかですよ。
これらについて、ドキュメントにはこう書かれていました。(コピペ)
DirectX エフェクトは、HLSL で記述された表現とエフェクト フレームワーク固有の構文で設定されたパイプライン ステートの集合です。
テクニックとは、レンダリング パスの集合です (1 つ以上のパスが存在する必要があります)。
エフェクト内のパイプライン ステートの一つです。
初心者にはわかりずらいでしょ、この文章…
あの、HLSL ってのはシェーダーを扱う時に使う言語を指してます。
つまり…そのシェーダー言語で書かれた一連の処理を「エフェクト」と呼び
「エフェクト」内のパイプラインの一つを「テクニック」と呼ぶみたいですね。
そんなわけで、.fx ファイルから名前を指定してテクニックへのアクセス子を取得しています。
次の2つが.fxファイルから作られます。
・ID3D10Effect* pEffect
・ID3D10EffectTechnique* pTechnique
また、.fxファイル内の変数(今回は行列)のアクセス子を取ってきてます。
名前やインデックスから取得なんて、ID3D10Effectがファイルを解析してデータで持っているイメージがつかめます。
次に記述されているのが頂点レイアウトの作成です。
レイアウトの要素をシェーダの入力形式と合うようにして、記述しているようですね。
ほか、次のようにテクニックの0番目のパスのディスクリプタを取得し、レイアウトを作成しています。
ここで作られるのが、次の頂点レイアウトですね。
・ID3D10InputLayout* pVertexLayout
レイアウトを作ったらさっそく、デバイスに設定していますね。
続いてお決まりの、頂点バッファの作成とインデックスバッファの作成を行っています。
次の2つが作られます。
・ID3D10Buffer* pVertexBuffe
・ID3D10Buffer* pIndexBuffer
最後にトポロジの設定をしていますね。データのトポロジと合わせて三角形リストを指定します。
さて、ここまでデバイスの初期化と入力アセンブラステージ、各シェーダのステージ(.fxファイルを確認しただけですが)を見てきました。
これが全7ステージですが、いかがでしたか?(やっぱ煩雑だよね…覚えることが多いね。)
ちょっと、まとめてみましょう!
次に列挙するのが、最低限必要となる変数です。
これらの作り方を、これまで示してきたんですね。
バックバッファであるスワップチェインを画面解像度から作成し
中にあるバッファをリソースとして扱いレンダリングターゲットとして登録する。
深度ステンシルも同サイズのリソースとして用意して、ステンシルビューとして出力結合ステージへ登録。
デバイスはハードウェアに依存して作られ、エフェクトは.fxファイルから作られ
テクニックとシェーダで使う変数はエフェクトから作られる。
どうです?ほんの少しスッキリしました?(いや、初見の方はわからんかもしれません)
では、描画関数を確認して本レッスンを締めくくりましょう!
ということで、次が描画関数です。
レンダリングターゲットと深度ステンシルビューをクリアして、シェーダで使う行列を設定してます。
テクニックを、テクニックのパスの数(今回は1個ですね)だけ繰り返し適用しながら
インデックスバッファの先頭から36個のインデックスを描画する命令を出しています。
最後にスワップチェインのバックバッファをフロントバッファへ切り替える関数を呼んでいます。
…と、まぁこんな感じでDirectX 10での3Dオブジェクトの描画の流れがつかめたでしょうか?
え?、行列って何ですかって?
そんな方は23話を読み返せば大丈夫です。
今回の DirectX 10 の入門講座をもって具体的にわかってきたことを挙げてみましょう。
ゲーム中に複数のオブジェクトを描画することが考えられますが、その場合
インデックスバッファに全てのオブジェクトの情報を納めておき(頂点バッファにもね)
描画時はオフセットとインデックス数を指定して描画するという手法が見えてきます。
決して、バッファを再構築しながら描画するなんて考えちゃだめだよ。
毎度ロードを挟むようなものなので、非常に低速なゲームになってしまうことが予想できます。
さて、DirectX 10 の描画における基礎を学んだところで、3D描画をもっと簡単にする独自の
ライブラリを作成してみましょう。
それでは次回を、お楽しみに!
2010/10/17 初記。
2010/10/31 追加。
2010/11/03 追加。