プログラム編

ゲーム製作日誌 プログラム編 No.10-3 「GPUにヘルプを渡す」


 前回は画面に表示させる頂点をデータとして用意したので、今回はそれをGPUにも読んで解釈できるように、読み方(ヘルプ)を伝える処理を扱っていきます。


1,頂点の構成情報をまとめる意味とは


 前回、頂点一つ一つを構造体としてまとめてデータを作成しました。それを実際に三角形の描画を行うGPUに転送する必要があります。

vertexwhat
 しかし、転送するときは頂点一つ一つを区切ってわかるように書き込まれるのではなく、すべての頂点が連続してメモリに書き込まれていきます。また座標や色等のデータも続けて書き込まれるために、もはやデータが送られてきたGPUにはどこをどう読めばいいのかわからない状態になってしまいます。


vertexgive
 それでもGPUに、読み方の指南書を渡してあげれば話は別です。上図のような情報を与えておけば「0~12の位置に座標が書いてあって、12~28には色が書いてあるんだな」と解釈できるわけです。
 そのため、頂点の座標や色がどの部分に書き込まれているのかを事前にGPUに伝えることが必要となるのです。今後はその情報のことを「頂点の構成情報」と呼びますが、まずはその情報の作り方から見て行きましょう。



2,頂点の構成情報を作る

 頂点の構成情報を作るためには、まず頂点の構成要素(座標や色)一つ一つがどこに書き込まれていて、どんな役割を持つのかをVertexElementを作成して決めていきます。


VertexEle
 これがVertexElementのコンストラクタです。いま気にするべきは2,3,5行目です。
 
  • 2行目には、データが始まる番地を書く
    vertexgive
    例えば、座標はこの表でいうと「0」番目から始まっています。ですので座標用のVertexElementを作成するときは、2行目の引数を「0」に設定するわけです。色でしたら「12」になりますね

  • 3行目には、データの型を書く

    データの型は、C#でいうint,float,string等と同じものです。3つ要素(xyz)があるものなら「.Float3」と、4つ要素(rgba,xyzw)があるものなら「.Float4」と設定します。つまり座標なら3の方で、色なら4ですね。

  • 4行目には、使用目的を書く

    座標だったら「Position」とか、色だったら「Color」と書いておきます。まんまです。


 まとめると以下の様な定義をしてあげれば良いわけです。
//座標用
new VertexElement(0, 0, DeclarationType.Float3, DeclarationMethod.Default, DeclarationUsage.Position, 0)

//色用 new VertexElement(0, 12, DeclarationType.Float4, DeclarationMethod.Default, DeclarationUsage.Color, 0)


 次に、作った情報をすべて配列にまとめます。その際、配列の最後の要素には終わりの合図としてVertexElement.VertexDeclarationEndをいれておきます。作った配列は、前回作った頂点構造体の中に入れておくと扱いが楽です。
[StructLayout(LayoutKind.Sequential)]
public struct PositionColor
{
    public Vector3 Position;
    public Vector4 Color;
    public static readonly VertexElement[] Elements =
{
new VertexElement(0, 0, DeclarationType.Float3, DeclarationMethod.Default, DeclarationUsage.Position, 0), new VertexElement(0, 12, DeclarationType.Float4, DeclarationMethod.Default, DeclarationUsage.Color, 0), VertexElement.VertexDeclarationEnd
}; }


 ここまでで作ったVertexElement配列を使って、VertexDeclarationというものを作成します。これがGPUに与える指南書になります。VertexDeclarationは、Direct3D9デバイスを初期化してからでなければ作れません。
VertexDeclaration vertexDec = new VertexDeclaration(D3Device, PositionColor.Elements);


4,まとめ

 GPUには、頂点データを連続的に書き並べたバイト列のみが転送されてくるため各要素の位置や意味をそれだけでは解釈することができません。
 そこで、あらかじめ解釈するためのヘルプのようなものを渡しておくことで、GPUにも頂点の意味を理解することが可能になります。今回は、そのヘルプの作成の仕方を扱いました。
 次回は、解釈の仕方が分かったGPUに頂点データを送ってみようと思います。

P.S.
 今回が一番の山場だったので次回は今回苦労した分楽になる予定?
 

ゲーム製作日誌 プログラム編 No.10-2 「頂点配列の用意」


 Direct3Dの複雑さが見えた前回に引き続き、今回は画面に表示させる頂点の用意をするところまで解説していきます。


1,頂点の構造体を用意する


 画面に三角形を描くことを目的としているため、まずはどのような三角形を描くかを定義する必要があります。

 Direct3Dでは、三角形自体に情報を与えずにそれを構成する3つの頂点1つ1つに情報を与えてそのような三角形にするのか定義します。

tri_vertex


 3つの頂点に別々に情報を与えて描画させると、Direct3Dは点の間の情報を補間して三角形内部の様子がしだいに移り変わっていくように描画してくれます。図の場合は色がグラデーションがかかったかのように描画されています。点の色を変えるだけでグラデーションできるなんて簡単でいいですね。

 この情報の補間機能ですが、色だけではなくほかにもテクスチャ座標や法線ベクトル等にも適応されます。そのことは今後扱っていきましょう。




 さて、三角形を作るには頂点を決めればいいということなので、まずは頂点1つをあらわす構造体を作成します。


[StructLayout(LayoutKind.Sequential)] public struct PositionColor { public Vector3 Position; public Vector4 Color; }

 Vector3やVector4型は、実数型floatを数個そなえているもので座標や色をあらわすときに使います。座標であれば順にx,y,zと割り当て、色であればr,g,b,aというように割り当てます。

 つまり、この構造体は頂点に座標と色を与えることができるそういった定義なわけです。


 構造体の前につけた
[StructLayout(LayoutKind.Sequential)]
は構造体の中身をメモリに記録する時に、定義した順に配置することを約束する属性です。おまじないなのでつけておきます。




2,頂点配列の作成

 構造体を定義し、頂点を表すことができるようになったため描画したい三角形の3つの頂点を表現したいと思います。

PositionColor[] tri_vertices = new PositionColor[3];

tri_vertices[0] = new PositionColor();
tri_vertices[0].Position = new Vector3(0f, 0.5f, 0f); //上
tri_vertices[0].Color = new Vector4(255f / 255f, 201f / 255f, 14f / 255f, 1f); //オレンジ

tri_vertices[1] = new PositionColor();
tri_vertices[1].Position = new Vector3(-0.5f, -0.5f, 0f); //左下
tri_vertices[1].Color = new Vector4(255f / 255f, 201f / 255f, 14f / 255f, 1f); //オレンジ

tri_vertices[2] = new PositionColor();
tri_vertices[2].Position = new Vector3(0.5f, -0.5f, 0f); //右下
tri_vertices[2].Color = new Vector4(255f / 255f, 0f / 255f, 0 / 255f, 1f); //赤

 先ほど定義した構造体の要素数3の配列を作成し、3つの頂点を決めました。座標や色は以下の形式に従って入力していきます。
vertex.Position = new Vector3(X座標, Y座標, Z座標);
vertex.Color = new Vector4(赤成分, 緑成分, 青成分, 透明度);
 ここで注意しなくてはならないことは、色成分は0~1の範囲に直さなければいけないことと、XYZ座標空間が多少変わっていることの2点です。
proj_space
 黒枠は、画面の枠です。そうしたとき画面の左上が(-1,1)、右下が(1,-1)となるような空間が設定されていてそこに三角形を配置していくことになります。間違っても画面のピクセル座標にあわせないようにしてください。

 この変わった座標配分の話は、今後変換行列のところですることになると思います。




3、まとめ

 今回で表示する三角形の頂点を定義することができました。次回はこの配列のデータをグラフィックボードに転送する処理を扱っていきます。

ゲーム製作日誌 プログラム編 No.10-1 「ポリゴン描画の全体の流れ」


 今回と次回では、Direct3Dを使って画面に三角形を描画するまでを扱っていきたいと思います。やることにしては意外とやることが多く、また後々の基本になることになるので何回かに分割して説明していきます。


1,ポリゴン描画の流れ

 「ポリゴン」とは、複数の点を結んだ線で囲まれた多角形のことを言います。

 例えば「三角ポリゴン」といえば、3つの頂点を結んだ三角形のことでして、今後はその三角形のみを扱っていきます。なぜ三角ポリゴンしか使わないかといえば、自分たちが使おうとしているDirect3Dが三角形の表示しか扱っていないからです。基本的にどんな図形でも三角形を何個も並べれば形作れるのでそういう仕様になっているのでしょうね。

polygon


 そんな三角形をDirect3D9で描画するには以下の手順を踏まなければいけません。

  1. 頂点自体のデータを用意する

    ・頂点1要素を表す構造体を定義する

    ・構造体の配列を作って全頂点のデータを作成する


  2. 頂点データが記録された位置を定義する

    ・頂点の各項目がどの位置に記録されているかをVertexElementで指定する

    ・指定したVertexElementでVertexDeclarationを作成する


  3. 頂点をグラフィックボードに送るための準備

    ・専用の格納場所VertexBufferを作成する

    ・頂点の配列をVertexBufferに書き込む


  4. 頂点を画面に表示するためのシェーダを用意する

    ・シェーダをコンパイルする

    ・シェーダの実体であるEffectを作成する


  5. 画面に描画する

    ・描画対象の空間(世界)の状況を設定する

    ・描画する


  6. 不必要なものを破棄する

    ・VertexBufferとVertexDeclarationとEffectを破棄する



 全体的に見ても、たかが三角形の描画でいろいろな準備が必要となっています。はっきりいってめんどうくさそうですね。
 でも拡張性つまり多種なシーンを作り出すためにここまで複雑な作りになっているわけですから、基本を大切にすべく次回からは一つ一つじっくり解説していきます。 

ゲーム製作日誌 プログラム編 No.9 「Direct3D9デバイスの作成」

今回は、前回解説して出現させた「ウィンドウフォーム」からDirect3D9デバイスを作成する方法を解説します。


1、デバイスの作成方法

 Direct3D9デバイスの作成手順は以下のとおりになっています。

//Direct3D9デバイスを作成する
Device device; //代入用変数を用意
Direct3D direct3D = new Direct3D(); //Direct3Dインターフェースを作る(おまじない)
PresentParameters pp = new PresentParameters(); //デバイス作成にあたっての設定を容易

...

//上記で設定したパラメータでデバイスを作成
device = new Device(direct3D, 0, DeviceType.Hardware, frm.Handle, CreateFlags.HardwareVertexProcessing, pp);

 ちなみに、このコードは前回配布したソースコードを抜粋したものです。各行について細かく説明をしていきます。

  1.  Direct3Dインターフェースを作成する(おまじない)
    Direct3D direct3D = new Direct3D(); //Direct3Dインターフェースを作る(おまじない)
     SharpDX.Direct3D9名前空間内Direct3Dクラスのインスタンスを作成しておきます。このインスタンスがないと、Direct3D9デバイスが作成できないので必ず作成しておく必要があります。


  2. 描画設定リストを作る
    PresentParameters pp = new PresentParameters(); //デバイス作成にあたっての設定を容易
     Direct3D9デバイスを作成するには、描画設定を予め伝えておかなくてはなりません。そのため、描画設定を格納する変数を用意します。型はそれ専用のPresentParameters(名前空間:SharpDX.Direct3D9)です。

  3. 描画設定(バックバッファ)を設定する
    //バックバッファ関連
    pp.BackBufferCount = 1; //バックバッファを一枚用意
    pp.BackBufferFormat = Format.A8R8G8B8; //バックバッファに保管する色要素はアルファ抜きRGB
    pp.BackBufferWidth = frm.ClientSize.Width; //バックバッファの幅
    pp.BackBufferHeight = frm.ClientSize.Height; //バックバッファの高さ
     バックバッファとは、画面に直接描画するのではなく、代わりにメモリに書き込む機能です。この機能を利用することでCPUの処理とGPUの処理を分離して、全体的な流れの高速化が見込めます。また、メモリに完全に書き込んでから一気に画面に表示するので”ちらつき防止”の意味もあります。
     設定はソースコードを参照してください。バックバッファの大きさは、フォームの大きさを指定してください。

  4. 描画設定(Zバッファ)を設定する
    //Zバッファとステンシルバッファの設定
    pp.AutoDepthStencilFormat = Format.D24X8; //Zバッファに24ビット,ステンシルバッファに8ビットの精度を与える
    pp.EnableAutoDepthStencil = true; //Zバッファとステンシルバッファを有効にする
     Zバッファとは、一度書き込んだポリゴンのピクセルの遠さを記録しておくメモリです。これを有効にしておくと、遠くのものから近くのものにと順序に沿って物体を描画する必要がなくなります。Format.D16もありますが、精度不足に陥ることがよくあるのでD24X8を指定してください。

  5. 描画設定(ウィンドウかフルスクリーンか)を設定する
    //描画ウィンドウの設定
    pp.Windowed = true; //ウィンドウモードで設定
     画面にどのように絵を描画するか設定します。この設定をfalseにするとフルスクリーンモードで描画ができるのですが、今回はウィンドウフォームを用いた描画なので解説しません。

  6. 描画設定(フレーム更新タイミング)を設定する
    //フレーム更新タイミング関連
    pp.PresentationInterval = PresentInterval.Default; //ディスプレイの垂直同期に合わせて更新
     描画した絵をどのタイミングで画面に表示させるか設定します。Defaultでは、ディスプレイの表示更新タイミングに合わせて表示させます。Immediateでは、ディスプレイを待たずに直ちに画面に反映させます。
     このような性質上、ディスプレイの表示周波数によって設定をDefaultにした場合は、FPSがそれを超えることはありません。

  7. 描画設定(アンチエイリアス)を設定する
    //アンチエイリアス設定
    pp.SwapEffect = SwapEffect.Discard;
    pp.MultiSampleType = MultisampleType.None; //アンチエイリアス無効
     描画したポリゴンの縁がピクセル単位にまとめられてギザギザする減少を回避する機能がアンチエイリアスです。サンプルでは無効にしましたが、有効にしたい場合は、
    pp.MultiSampleType = MultisampleType.FourSamples; //アンチエイリアス有効
    などと指定してください。

  8. デバイスを作成する
    //上記で設定したパラメータでデバイスを作成
    device = new Device(direct3D, 0, DeviceType.Hardware, frm.Handle, CreateFlags.HardwareVertexProcessing, pp);
    Direct3Dインターフェースと、フォームのハンドル、描画設定リストを使ってデバイスのコンストラクタを呼び出します。これが完了すれば、Direct3D9デバイスを取得することができ、いよいよ画面への描画が可能になってくるのです。 

2、総括

 今回はDirect3D9デバイスの作成方法と、そのための描画設定リストの設定項目について確認しました。設定項目の中には、解説不足なものも多くあったと思うので、今後それぞれ詳しく解説していきたいと思います。
 次回からはこのDirect3D9デバイスを利用して画面への描画へと進んでいきます。

ゲーム製作日誌 プログラム編 No.8 「描画ウィンドウの表示&サンプル配布」

こんにちは。
今回は、前回確認したDirect3Dによる描画の流れの一つひとつについて掘り下げていきます。

1、サンプルプログラム配布

 今回以降の説明では、前回の内容通りに作成したあるひとつのプログラムのソースコードを参照しながら解説をしていきたいと思います。
 プログラムのソースコードを入手するには、ここのURLを参照してください。
  解説用ソース群URL:https://docs.google.com/uc?authuser=0&id=0B7rzXohtSxChZXpDMUdPeUxFVDQ&export=download

 配布ファイルを展開すると、「Visual Studio 2010」のC#プロジェクトが作成されます。プログラムの内容は、Direct3Dを利用してウィンドウに時間ごとに異なった色を表示するものとなっています。終了キーは[X]です。


2、ウィンドウフォームの表示

  •  Direct3Dを使って描画をするためには、Direct3Dデバイスが必要です。
  •  Direct3Dデバイスを初期化・手に入れるためには、描画先のウィンドウフォームが必要です。
 こういった依存関係があるため、まずは、描画先のウィンドウフォームを出すための手順から解説していくことにします。

 ウィンドウフォームとは、画面中に出てくる窓のことで下の図の白い枠で囲まれた領域のことを言います。Windowsを使ってればわかりますよね?
ClientPosition

 このウィンドウフォームを出現させるための便利な機能がいくつかVisual C#についています。出現させる流れとしては、プロジェクトにウィンドウフォームを作成して、コードにそれを表示させる記述をする、といった流れになっています。

 ・ウィンドウフォームを作成する手順
  1.  「ソリューションエクスプローラ」でプロジェクトを右クリックして「追加」→「Windows フォーム」をクリックする。

  2. 適当な名前(FrmScreen等...)をつけて、「追加」を押す

 これでウィンドウフォームをプロジェクトに作成することができます。作成されたウィンドウフォームは、「ソリューションエクスプローラ」内に表示されてダブルクリックをすることで、編集することができます。

 ゲームで主に必要となってくることは、右上に出てくるボタンの種類やウィンドウサイズの変更が可能かなどでしょう。



 ・ウィンドウフォームを出現させるためのコード

  ここで先ほど配布したコードが登場してきます。Program.cs内の行19からです。配布したコードでも、起動直後にすぐウィンドウフォームを表示しようとしています。


//描画フォームを初期化します
FrmScreen frm = new FrmScreen();
frm.ClientSize = new System.Drawing.Size(1280, 720); //フォームサイズは、ClientSizeプロパティで変更
frm.Show(); //フォームを表示しておく

  1.  まずはフォームのインスタンスを作る。
    FrmScreen frm = new FrmScreen();
    こんなコードで実現できます。「FrmScreen」とは、先ほどプロジェクトに作成したウィンドウフォームの名前です。

  2. 表示するフォームの大きさを決める。
    frm.ClientSize = new System.Drawing.Size(1280, 720);
    フォームの大きさ、つまり描画領域の大きさですね。描画解像度とも言いますかね。その解像度を指定しているのがこの行です。フォームのSizeプロパティに代入するのではなく、ClientSizeプロパティに設定してあげてください。
    違いはなにかと申しますと、Sizeプロパティの方はタイトルバーやウィンドウの縁も合わせたウィンドウサイズで、ClientSizeプロパティはそれらを除いた純粋な領域です。自分は最初ここでハマりました...。

  3. ウィンドウを実際に出現させる。
    frm.Show();
    まんまですね。これでウィンドウフォームを画面に出現させることが出来ました。ShowDialog()メソッドの方を呼び出してしまうと、ウィンドウが閉じるまで処理を継続できないので注意してください。


 今回は、描画領域として必要なウィンドウフォームを出現させる方法を解説しました。次回は、そのフォームを元にDirect3D 9デバイスを作成する方法を解説していきます。

twitterはこちら
ニコ生放送します
ギャラリー
  • ウマモサク
  • ウマモサク
  • ウマモサク
  • 3/17 制作記録
  • 3/17 制作記録
  • C91告知
  • C91告知
  • C91告知
  • C91告知
アクセスカウンター
  • 今日:
  • 昨日:
  • 累計:

  • ライブドアブログ