インデックス
四角形の描画と頂点の重複
プリミティブの種類は線または三角形の組み合わせしか存在しません。よって、長方形を描画したい場合は 2 つの三角形を組み合わせます。このとき、2 つの三角形を正直に作った場合は 6 つの頂点が必要になります。しかし、長方形を構成する 2 つの三角形の 2 つの頂点は同じ座標をとなります。より複雑な図形となっていった時に、同じ座標の頂点を何度も用意するのは効率的ではありません。
長方形に必要な頂点座標は、左上隅、右上隅、左下隅、右下隅の 4 点です。このような場合、インデックスを使うことで 4 つの頂点だけで長方形を表現することができます。
インデックスは、単に頂点バッファを参照する番号を集めた整数型の配列です。インデックスを用いた描画では、頂点配列が順にプリミティブとして描画されるのではなく、頂点配列に保存されている個々の要素を指すインデックスを用いて描画します。座標や色などの多くの情報を持つ頂点を重複させるよりも、軽い整数のインデックスを用いた方が使用するデータ量を減らすことができます。
インデックスを用いて頂点データの配列からプリミティブを描画するには DrawUserPrimitives() メソッドに代わって DrawUserIndexedPrimitives() メソッドを使います。この場合、メソッドに描画するプリミティブの頂点データの配列とインデックスの配列の両方を渡します。
public void DrawUserIndexedPrimitives<T> ( PrimitiveType primitiveType, T[] vertexData, int vertexOffset, int numVertices, short[] indexData, int indexOffset, int primitiveCount ) where T : ValueType
public void DrawUserIndexedPrimitives<T> ( PrimitiveType primitiveType, T[] vertexData, int vertexOffset, int numVertices, int[] indexData, int indexOffset, int primitiveCount ) where T : ValueType
オーバーロードされている 2 つのメソッドの違いは、第 5 パラメータの indexData の型だけです。インデックスには 16 ビットの short 型と、32 ビットの int 型のいずれかを使用できます。通常は 16 ビットの short 型の方がメモリの消費を抑えられますが、32767 個以上の頂点を扱う場合は int 型が必要になります。
primitiveType パラメータには、プリミティブの種類を指定します。vertexData に、頂点データの配列を指定します。プリミティブの描画にはインデックスが用いられるため、頂点データの配列に格納されている頂点の順番は描画には関係しません。vertexOffset パラメータには、vertexData パラメータに指定された配列のオフセットを指定します。numVertices パラメータには、読み込む頂点の数を指定します。
indexData パラメータに、インデックスの配列を指定します。indexOffset パラメータには indexData パラメータに指定した配列から、使用する最初の要素を表すオフセットを指定します。最後の primitiveCount パラメータには、描画するプリミティブの数を指定します。
メソッドは、この配列に格納されている整数を、先頭から順に頂点配列を指すインデックスとして用います。例えば、indexData パラメータに指定した整数の配列の値が、順に 0、1、2 であった場合、vertexData パラメータに指定された頂点配列のうちの 0 番要素、1 番要素、2 番要素を使うことを表します。インデックスは重複させてもかまいません。
using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; public class TestGame : Game { public static void Main(string[] args) { using (Game game = new TestGame()) game.Run(); } private GraphicsDeviceManager graphicsDeviceManager; private BasicEffect effect; private VertexPositionColor[] vertices; private short[] indices; public TestGame() { graphicsDeviceManager = new GraphicsDeviceManager(this); Content.RootDirectory = "Content"; } protected override void Initialize() { vertices = new VertexPositionColor[] { new VertexPositionColor(new Vector3(-0.5F, 0.5F, 0), Color.Red), new VertexPositionColor(new Vector3(0.5F, 0.5F, 0), Color.Blue), new VertexPositionColor(new Vector3(-0.5F, -0.5F, 0), Color.Lime), new VertexPositionColor(new Vector3(0.5F, -0.5F, 0), Color.Black), }; indices = new short[] { 0, 1, 2, 1, 3, 2 }; base.Initialize(); } protected override void LoadContent() { effect = new BasicEffect(GraphicsDevice); effect.VertexColorEnabled = true; base.LoadContent(); } protected override void Draw(GameTime gameTime) { GraphicsDevice.Clear(Color.White); foreach (EffectPass pass in effect.CurrentTechnique.Passes) { pass.Apply(); GraphicsDevice.DrawUserIndexedPrimitives( PrimitiveType.TriangleList, vertices, 0, vertices.Length, indices, 0, 2); } base.Draw(gameTime); } }
コード1は、DrawUserIndexedPrimitives() メソッドを用いて、頂点データの配列とインデックスの配列から長方形を描画しています。この長方形は 2 つの三角形プリミティブで構成されています。頂点の配列には、4 つの要素しか指定していないことに注目してください。この 4 つの頂点は、描画する長方形の 4 つの角の座標と色を表しています。三角形のプリミティブは、この 4 つの頂点を参照するインデックスを用いて描画しています。
インデックスは short 型の配列として indices フィールドに保存しています。この配列の要素は、先頭から順に 0、1、3、0、3、2 という値を保存しています。これらが、プリミティブの描画時に頂点データを参照するインデックスとして用いられます。よって、描画される 2 つの三角形の頂点は、順に vertices[0]、vertices[1]、vertices[2]、vertices[0]、vertices[3]、vertices[2] という順番で入力されています。