WisdomSoft - for your serial experiences.

頂点バッファ

DrawUserPrimitives() メソッドによる描画はアプリケーションのメモリ領域に保存されている頂点配列をグラフィックスデバイスに送信にて描画されます。頂点が変更されない場合、描画毎にメモリとグラフィックスデバイスで頂点配列を取引するのは非効率的なので、グラフィックスデバイスの頂点バッファとして頂点配列を設定します。

頂点配列をデバイスに設定する

これまで、プリミティブの描画は DrawUserPrimitives() メソッド、または DrawUserIndexedPrimitives() メソッドを使って行いました。メモリ上にある頂点データを頻繁に更新しながら描画する場合は、この方法が適しています。DrawUserPrimitives() メソッドとDrawUserIndexedPrimitives() メソッドは、頂点配列やインデックス配列を、メソッドを呼び出すたびにグラフィックスデバイスに送信します。元のデータを保持する必要がないため、自由に頂点データを編集できます。

しかし、描画するたびにメモリからグラフィックスデバイスに頂点データが入力されるため、頂点配列やインデックス配列に変更がない場合は非効率的です。初期化以降、頂点が変化しない場合は、事前に頂点データをグラフィックデバイスに設定し、その後の描画は設定されている頂点データを用いるべきです。

一般に、頂点の列を保存するメモリを頂点バッファと呼びます。頂点バッファを事前にグラフィックスデバイスに設定することで、頂点データと描画の処理を分離できます。頂点バッファをグラフィックデバイスに設定するには、最初に頂点の配列を保存する Microsoft.Xna.Framework.Graphics.VertexBuffer クラスのオブジェクトを作成します。

Microsoft.Xna.Framework.Graphics.VertexBuffer クラス
public class VertexBuffer : GraphicsResource

このクラスのコンストラクタは、バッファ用の領域を計算するために頂点の型と要素数を指定します。

VertexBuffer クラスのコンストラクタ
public VertexBuffer (
         GraphicsDevice graphicsDevice,
         Type vertexType,
         int vertexCount,
         BufferUsage usage
)

graphicsDevice には、この頂点バッファに関連付けられる GraphicsDevice オブジェクトを指定します。vertexType パラメータと vertexCount パラメータには、設定する頂点の型と要素数を指定します。usage パラメータには、この頂点バッファの使用方法を表す Microsoft.Xna.Framework.Graphics.BufferUsage 列挙型のいずれかのメンバを指定します。

Microsoft.Xna.Framework.Graphics.BufferUsage 列挙型
[FlagsAttribute]
public enum BufferUsage

この列挙型には、特に指定しない None メンバと、書き込み専用のバッファであることを表す WriteOnly メンバが定義されています。WriteOnly メンバが指定された場合は、頂点バッファからデータを読み取ることができなくなりますが、代わりに書き込みや描画に最適な場所にデータが配置される可能性があるため、パフォーマンスの向上を期待できます。

VertexBuffer インスタンスを生成することができれば、VertexPositionColor 構造体の配列など、事前に用意した頂点配列を設定します。頂点配列の設定は SetData() メソッドから行います。

VertexBuffer クラス SetData() メソッド
public void SetData<T> (T[] data) where T : ValueType

data パラメータに、設定する頂点の配列を指定します。ここで指定する頂点データの配列は、VertexBuffer クラスのコンストラクタで生成したサイズを超えてはいけません。VertexBuffer によって確保されるメモリは固定されています。

VertexBuffer オブジェクトを用意できれば、この頂点バッファをグラフィックスデバイスに設定しなければなりません。頂点バッファは SetVertexBuffer() メソッドから設定できます。

GraphicsDevice クラス SetVertexBuffer() メソッド
public void SetVertexBuffer (VertexBuffer vertexBuffer)

vertexBuffer パラメータに適切な頂点配列を設定されている VertexBuffer オブジェクトを指定します。

GrahpicsDevice の頂点ストリームに頂点バッファを設定することができれば、DrawPrimitives() メソッドで描画できます。DrawUserPrimitives() メソッドとは異なり設定された頂点バッファを用いて描画するため、頂点配列をパラメータで指定する必要はありません。

GraphicsDevice クラス DrawPrimitives() メソッド
public void DrawPrimitives (
         PrimitiveType primitiveType,
         int startVertex,
         int primitiveCount
)

primitiveType パラメータには、プリミティブの種類を指定します。startVertex パラメータには、使用する最初の頂点のインデックスを指定します。primitiveCount パラメータには、描画するプリミティブの数を指定します。このメソッドを呼び出すと、startVertex パラメータに指定したインデックスの頂点から、primitiveCount パラメータに指定したプリミティブの数だけ描画します。プリミティブを描画するのに十分な数の頂点が設定されていなければなりません。

コード1
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 VertexBuffer vertexBuffer;
    private BasicEffect effect;
    private VertexPositionColor[] vertices;

    public TestGame()
    {
        graphicsDeviceManager = new GraphicsDeviceManager(this);
        Content.RootDirectory = "Content";
    }

    protected override void Initialize()
    {
        vertices = new VertexPositionColor[] {
	        new VertexPositionColor(new Vector3(0, 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),
        };
        base.Initialize();
    }

    protected override void LoadContent()
    {
        vertexBuffer = new VertexBuffer(GraphicsDevice, typeof(VertexPositionColor), vertices.Length, BufferUsage.None);
        vertexBuffer.SetData(vertices);
        GraphicsDevice.SetVertexBuffer(vertexBuffer);

        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.DrawPrimitives(PrimitiveType.TriangleList, 0, 1);
        }

        base.Draw(gameTime);
    }
}
実行結果
コード1 実行結果

コード1は DrawPrimitives() メソッドを用いて頂点バッファに保存されている頂点配列を用いてプリミティブを描画しています。LoadContent() メソッドで事前に頂点バッファを GraphicsDevice オブジェクトに設定している点に注目してください。DrawUserPrimitives() メソッドとは異なり、描画するたびに頂点配列をメモリとグラフィックスデバイスの間で取引する必要が亡くなります。