インデックスバッファを使用したボックスの描画
今までは三角形や四角形など少ないポリゴンを使っていましたが、今回は少し多めに使用してボックスを作成します。また、効率よく頂点データを作成するために「インデックスバッファ」というものを使用してみたいと思います。

下のリンクから今回のプロジェクトをダウンロードできます。
今回のメインコードファイルを載せます。重要なコードを赤色で表示させています。
MainClass.cs
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using Microsoft.DirectX;
using Microsoft.DirectX.Direct3D;
namespace MDXSample
{
/// <summary>
///
/// </summary>
public partial class MainSample : IDisposable
{
/// <summary>
/// 頂点バッファ
/// </summary>
private VertexBuffer _vertexBuffer = null;
/// <summary>
/// インデックスバッファ
/// </summary>
private IndexBuffer _indexBuffer = null;
/// <summary>
/// インデックスバッファの各頂点番号配列
/// </summary>
private static Int16[] _vertexIndices = new Int16[] { 2, 0, 1, 1, 3, 2, 4, 0, 2, 2, 6, 4,
5, 1, 0, 0, 4, 5, 7, 3, 1, 1, 5, 7, 6, 2, 3, 3, 7, 6, 4, 6, 7, 7, 5, 4 };
/// <summary>
///
/// </summary>
/// <param name="topLevelForm"></param>
/// <returns></returns>
/// <remarks>
///
/// </remarks>
public bool InitializeApplication(MainForm topLevelForm)
{
this._form = topLevelForm;
this.CreateInputEvent(topLevelForm);
try
{
this.CreateDevice(topLevelForm);
this.CreateFont();
}
catch (DirectXException ex)
{
MessageBox.Show(ex.ToString(), "エラー", MessageBoxButtons.OK, MessageBoxIcon.Error);
return false;
}
// 箱を作成するための頂点バッファを作成
// 箱の頂点は8個
this._vertexBuffer = new VertexBuffer(typeof(CustomVertex.PositionColored),
8, this._device, Usage.None, CustomVertex.PositionColored.Format, Pool.Managed);
// 8点の情報を格納するためのメモリを確保
CustomVertex.PositionColored[] vertices = new CustomVertex.PositionColored[8];
// 各頂点を設定
vertices[0] = new CustomVertex.PositionColored(-2.0f, 2.0f, 2.0f, Color.Yellow.ToArgb());
vertices[1] = new CustomVertex.PositionColored(2.0f, 2.0f, 2.0f, Color.Gray.ToArgb());
vertices[2] = new CustomVertex.PositionColored(-2.0f, 2.0f, -2.0f, Color.Purple.ToArgb());
vertices[3] = new CustomVertex.PositionColored(2.0f, 2.0f, -2.0f, Color.Red.ToArgb());
vertices[4] = new CustomVertex.PositionColored(-2.0f, -2.0f, 2.0f, Color.SkyBlue.ToArgb());
vertices[5] = new CustomVertex.PositionColored(2.0f, -2.0f, 2.0f, Color.Orange.ToArgb());
vertices[6] = new CustomVertex.PositionColored(-2.0f, -2.0f, -2.0f, Color.Green.ToArgb());
vertices[7] = new CustomVertex.PositionColored(2.0f, -2.0f, -2.0f, Color.Blue.ToArgb());
using (GraphicsStream data = this._vertexBuffer.Lock(0, 0, LockFlags.None))
{
data.Write(vertices);
this._vertexBuffer.Unlock();
}
// インデックスバッファの作成
// 第2引数の数値は(三角ポリゴンの数)*(ひとつの三角ポリゴンの頂点数)*
// (16 ビットのインデックスサイズ(2byte))
this._indexBuffer = new IndexBuffer(this._device, 12 * 3 * 2, Usage.WriteOnly,
Pool.Managed, true);
// インデックスバッファをロックする
using (GraphicsStream data = this._indexBuffer.Lock(0, 0, LockFlags.None))
{
// インデックスデータをインデックスバッファにコピーします
data.Write(_vertexIndices);
// インデックスバッファのロックを解除します
this._indexBuffer.Unlock();
}
this._device.RenderState.Lighting = false;
return true;
}
/// <summary>
///
/// </summary>
public void MainLoop()
{
// カメラの設定
this.SettingCamera();
this._device.Clear(ClearFlags.Target | ClearFlags.ZBuffer, Color.DarkBlue, 1.0f, 0);
this._device.BeginScene();
// 頂点バッファをデバイスのデータストリームにバインド
this._device.SetStreamSource(0, this._vertexBuffer, 0);
// 描画する頂点のフォーマットをセット
this._device.VertexFormat = CustomVertex.PositionColored.Format;
// インデックスバッファをセット
this._device.Indices = this._indexBuffer;
// レンダリング(描画)
this._device.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, 8, 0, 12);
this._font.DrawText(null, "θ:" + this._lensPosTheta, 0, 0, Color.White);
this._font.DrawText(null, "φ:" + this._lensPosPhi, 0, 12, Color.White);
this._font.DrawText(null, "マウス位置:" + this._oldMousePoint, 0, 24, Color.White);
this._device.EndScene();
this._device.Present();
}
/// <summary>
///
/// </summary>
public void Dispose()
{
// 頂点バッファを解放
if (this._vertexBuffer != null)
{
this._vertexBuffer.Dispose();
}
// インデックスバッファを解放
if (this._indexBuffer != null)
{
this._indexBuffer.Dispose();
}
if (this._font != null)
{
this._font.Dispose();
}
if (this._device != null)
{
this._device.Dispose();
}
}
}
}
|
では、赤文字の部分を説明していきます。MainSamplePartial.cs ファイルのコードはこちらです。
ボックスの構成について少し考えてみたいと思います。
まずボックスは6つの面から成り立っており、ひとつの四角形の面は今までやってきたとおり、三角ポリゴン2つで構成されています。ということは三角ポリゴンは合計で「2×6=12」枚となります。さらに三角ポリゴンの頂点は3つなので頂点の合計は「12×3=36」個となります。なので、「VertexBuffer」のみで作成する場合は、36個のデータを箱の形になるように位置情報を決めて書き込めば、箱として表示することは可能です。(TriangleStrip でも24個必要)
しかし、箱をイメージしてください。箱の角は8個です。位置情報としては8個で十分なはずです。頂点データというのは、数が増えてくるとメモリを圧迫します。これを何とか減らすために使用するのが「IndexBuffer」です。
位置情報は8個でいいのですが、ポリゴンの頂点は必ず36個必要になります。そこで、8個の頂点データを共有してしまいましょうというのが「IndexBuffer」の使用目的になります。

/// <summary>
///
/// </summary>
private VertexBuffer _vertexBuffer = null;
/// <summary>
///
/// </summary>
private IndexBuffer _indexBuffer = null;
/// <summary>
///
/// </summary>
private static Int16[] _vertexIndices = new Int16[] { 2, 0, 1, 1, 3, 2, 4, 0, 2, 2, 6, 4,
5, 1, 0, 0, 4, 5, 7, 3, 1, 1, 5, 7, 6, 2, 3, 3, 7, 6, 4, 6, 7, 7, 5, 4 };
|
コードでは「IndexBuffer」が宣言されていますが、その下に「頂点番号配列」をあらかじめ作成しています。これは36個の頂点分配列を確保していますが、各パラメータの意味は、各三角ポリゴンの頂点が8個の頂点データのうちの、何番目の頂点データを使用するかという意味になります。よく見るとわかりますが、中身のデータは「0~7」の間のインデックスが書き込まれています。各数字を下のテーブルのように並び替えるとわかりやすいでしょう。
2, 0, 1
1, 3, 2
4, 0, 2
2, 6, 4
5, 1, 0
0, 4, 5
7, 3, 1
1, 5, 7
6, 2, 3
3, 7, 6
4, 6, 7
7, 5, 4
|
ちなみに配列の型を「Int16[]」としていますが「short[]」でもかまいません(2バイト)。「int」(4バイト)で配列を作る場合もありますが、これは頂点数が「65535」を超える場合に使用します。そうでない場合は2バイトで配列を作成してください。4バイトだとメモリの無駄遣いになります。
this._vertexBuffer = new VertexBuffer(typeof(CustomVertex.PositionColored),
8, this._device, 0, CustomVertex.PositionColored.Format, Pool.Managed);
CustomVertex.PositionColored[] vertices = new CustomVertex.PositionColored[8];
vertices[0] = new CustomVertex.PositionColored(-2.0f, 2.0f, 2.0f, Color.Yellow.ToArgb());
vertices[1] = new CustomVertex.PositionColored(2.0f, 2.0f, 2.0f, Color.Gray.ToArgb());
vertices[2] = new CustomVertex.PositionColored(-2.0f, 2.0f, -2.0f, Color.Purple.ToArgb());
vertices[3] = new CustomVertex.PositionColored(2.0f, 2.0f, -2.0f, Color.Red.ToArgb());
vertices[4] = new CustomVertex.PositionColored(-2.0f, -2.0f, 2.0f, Color.SkyBlue.ToArgb());
vertices[5] = new CustomVertex.PositionColored(2.0f, -2.0f, 2.0f, Color.Orange.ToArgb());
vertices[6] = new CustomVertex.PositionColored(-2.0f, -2.0f, -2.0f, Color.Green.ToArgb());
vertices[7] = new CustomVertex.PositionColored(2.0f, -2.0f, -2.0f, Color.Blue.ToArgb());
|
頂点バッファの作成は基本的に変わりはありません。ただし、頂点の数は36個ではなく8個だけですみます。
this._indexBuffer = new IndexBuffer(this._device, 12 * 3 * 2, Usage.WriteOnly,
Pool.Managed, true);
|
「IndexBuffer」を使うので、IndexBuffer を作成しなければなりません。コンストラクタに渡すデータは下の通りです。
|
IndexBuffer コンストラクタ
|
| インデックスバッファの作成 |
| device |
Direct3D デバイスを渡します。 |
| sizeOfBufferInBytes |
インデックスデータのバイト数を渡します。上記のコメントどおりの計算で、合計で「72」を渡します。 |
| usage |
使用法。今回は「Usage.WriteOnly」を指定 |
| pool |
リソースを配置する有効なメモリ クラスの指定。特に無ければ「Pool.Managed」でよい。 |
| sixteenBitIndices |
インデックス バッファが 16 ビットのインデックスを格納する場合は true を指定 |
using (GraphicsStream data = this._indexBuffer.Lock(0, 0, LockFlags.None))
{
data.Write(_vertexIndices);
this._indexBuffer.Unlock();
}
|
「IndexBuffer」も「VertexBuffer」と同じように書き込むだけです。書き込むデータは、あらかじめフィールドで作成した配列データをそのまま渡します。ちなみに今回作成したインデックスの配列は、このプロジェクトで使用するボックス専用なので、形状が違うポリゴンになる場合は配列数やインデックスが変わりますので注意してください。
カメラの設定をまとめました。今後カメラはマウスで操作することにします。
this._device.SetStreamSource(0, this._vertexBuffer, 0);
this._device.VertexFormat = CustomVertex.PositionColored.Format;
this._device.Indices = this._indexBuffer;
this._device.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, 8, 0, 12);
|
最初の2行については今までと同じですが、インデックスバッファ用のコードが追加されます。
Device.Indices には使用するインデックスバッファをセットします。
また描画メソッドには「Device.DrawIndexedPrimitives」というメソッドを使用します。
|
Device.DrawIndexedPrimitives メソッド
|
| 頂点データとインデックスからポリゴンを描画します。 |
| primitiveType |
描画するプリミティブの種類。今回は「PrimitiveType.TriangleList」を使用 |
| baseVertex |
インデックス バッファの先頭から最初の頂点インデックスまでのオフセット。特に無ければ 0 |
| minVertexIndex |
呼び出しで使われる頂点の最小頂点インデックス。これも特に無ければ 0 |
| numVertices |
頂点データの数。基本的に頂点バッファに書き込んだ頂点数 |
| startIndex |
インデックス配列における頂点の読み出し開始位置。特に無ければ 0 |
| primCount |
レンダリングするプリミティブの数。今回三角形ポリゴン12枚なので「12」 |
if (this._vertexBuffer != null)
{
this._vertexBuffer.Dispose();
}
if (this._indexBuffer != null)
{
this._indexBuffer.Dispose();
}
|
最後に頂点バッファとインデックスバッファを破棄しています。
|